Common element fields#
Every element type — text, shape, image, video, audio, group, caption, particles — shares the fields on this page. Variant-specific fields live on each element's own reference page.
interface BaseElement { id?: string; name?: string; type: ElementType; layer: number; "tok-cmt">// required, unique per container visible?: boolean; time?: number | string; duration?: number | string | "tok-str">'auto' | "tok-str">'end'; "tok-cmt">// Transform (2D) x?: number | string | Keyframe[] | Expr; y?: number | string | Keyframe[] | Expr; x_anchor?: number | string; y_anchor?: number | string; width?: number | string | Keyframe[] | Expr; height?: number | string | Keyframe[] | Expr; aspect_ratio?: number; rotation?: number | Keyframe[] | Expr; "tok-cmt">// alias: z_rotation (same slot) scale?: number | Keyframe[] | Expr; "tok-cmt">// Transform (3D — only meaningful under a camera) z?: number | Keyframe[] | Expr; x_rotation?: number | Keyframe[] | Expr; y_rotation?: number | Keyframe[] | Expr; x_scale?: number | string | Keyframe[] | Expr; y_scale?: number | string | Keyframe[] | Expr; x_skew?: number | Keyframe[] | Expr; y_skew?: number | Keyframe[] | Expr; "tok-cmt">// Visual opacity?: number | Keyframe[] | Expr; blend_mode?: "tok-str">'normal' | "tok-str">'multiply' | "tok-str">'screen' | "tok-str">'add' | "tok-str">'overlay' | "tok-str">'hard-light' | "tok-str">'soft-light'; "tok-cmt">// Filters blur_radius?: number | Keyframe[] | Expr; brightness?: number | Keyframe[] | Expr; contrast?: number | Keyframe[] | Expr; saturation?: number | Keyframe[] | Expr; hue_rotate?: number | Keyframe[] | Expr; "tok-cmt">// Effects & material effects?: Effect[]; material?: Material; "tok-cmt">// Animation animations?: Animation[]; keyframe_animations?: KeyframeAnimation[]; }
Identity#
| Field | Type | Default | Description |
|---|---|---|---|
id | string | auto | Stable identifier. Used by the MCP tools edit_element / delete_element and by keyframe_animations.property references. If omitted, the runtime assigns one. |
name | string | — | Optional human-readable label. Has no rendering effect. |
type | ElementType | required | One of 'video', 'image', 'text', 'shape', 'audio', 'group', 'caption', 'particles'. Discriminates which variant fields apply. |
Timing#
| Field | Type | Default | Description |
|---|---|---|---|
layer | number | required | The element's layer in the stack, integer 1–1000, required and unique per container (like CSS z-index). HIGHER numbers draw in front — the highest layer is on top (backgrounds go at layer 1). Paint order is ascending layer (layer 1 is painted first/farthest back). z (depth) takes precedence whenever depth sorting is active — under a camera (unless camera.sort: "paint"), or, with no camera, whenever any element carries depth (z / 3D fields); with all z at 0 it is pure layer order. |
visible | boolean | true | When false, the element is skipped entirely (not rendered, no audio). |
time | number | string | 0 | When the element appears, in seconds (or "50%" of the Source duration). |
duration | number | string | 'auto' | 'end' | 'auto' | How long the element stays. 'auto' uses the natural duration of media; 'end' extends to the Source's end. |
Generated sets#
Image, text, shape and group elements. One grid, chart, step sequence, slideshow or word sequence is one element (PROTOCOL §3.7).
| Field | Type | Default | Description |
|---|---|---|---|
repeat | number | — | Render the element as a generated set of N identical copies (integer 2–500). Per copy: expressions see i = copy index (0-based) and n = copy count; {i} / {i1} placeholders substitute in string fields. Copies share the layer, draw in i order, ids get a #k suffix. A group's children inherit the copy's scope. |
repeat_data | row[] | — | One copy per row — a partial element patch + a variable scope: row keys naming element fields (time, duration, text, fill_color, …) override that field for the copy; ALL row keys are in scope for the copy and its descendants as {key} in strings and as bare identifiers in expressions (numeric values only). Mutually exclusive with repeat. Structural keys and expression-reserved names are rejected. |
repeat_stagger | number | 0 | Seconds between successive copies: copy k starts at time + k * repeat_stagger. Requires a generated set and a numeric time; a row's own time wins. |
style | string | — | Name of a Source-level styles bundle merged UNDER this element's own fields (defaults < style < element; no cascade). Appearance only; unknown names fail validation. Image/text/shape only (PROTOCOL §2.3). |
Transform#
Positions use the top-left convention by default (the CSS / SVG / Canvas model): (x, y) is where the element's anchor lands on the canvas, and the anchor defaults to the element's top-left corner unless x_anchor / y_anchor say otherwise. So x: 0, y: 0 is the top-left corner and a full-frame layer is just x: 0, y: 0, width: W, height: H. Set x_anchor: '50%', y_anchor: '50%' to position by center. Rotation and scale always pivot the element's center, independent of the anchor.
| Field | Type | Default | Description |
|---|---|---|---|
x | number | string | Keyframe[] | Expr | 0 | Horizontal position of the anchor. Number = pixels; string = '50%', '100vw', etc. With the default anchor (left), this is the box left edge. |
y | number | string | Keyframe[] | Expr | 0 | Vertical position of the anchor. With the default anchor (top), this is the box top edge. |
x_anchor | number | string | 0 ('0%') | Anchor offset within the element's box. '0%' = left edge (default), '50%' = center, '100%' = right edge. |
y_anchor | number | string | 0 ('0%') | Anchor offset within the element's box. '0%' = top (default), '50%' = center, '100%' = bottom. |
width | number | string | Keyframe[] | Expr | content-derived | Width of the element's box in px or a string like '50%'/'100vw'. |
height | number | string | Keyframe[] | Expr | content-derived | Height of the element's box in px or a string like '50%'/'100vh'. |
aspect_ratio | number | — | Optional fixed width-to-height ratio. |
rotation | number | Keyframe[] | Expr | 0 | In-plane rotation around the box center, in degrees. |
scale | number | Keyframe[] | Expr | 1 | Uniform scale around the box center. Multiplied with x_scale/y_scale. |
3D transform#
These only have a visible effect under a Source camera (see PROTOCOL.md §4.4); otherwise they default to identity.
| Field | Type | Default | Description |
|---|---|---|---|
z | number | Keyframe[] | Expr | 0 | Depth in px toward (+) / away from (−) the viewer. Any nonzero z activates depth sorting even without a camera (higher z = nearer = on top, overriding layer); under a camera it also drives perspective foreshortening. camera.sort: "paint" pins pure layer order. |
z_rotation | number | Keyframe[] | Expr | 0 | Same slot as rotation (in-plane degrees about the box center). Author one, not both — supplying both is a validation error. |
x_rotation | number | Keyframe[] | Expr | 0 | Rotation about the local x axis in degrees; tips the top edge away under a camera. |
y_rotation | number | Keyframe[] | Expr | 0 | Rotation about the local y axis in degrees; turns the right edge away under a camera. |
x_scale | number | string | Keyframe[] | Expr | 1 | Horizontal scale factor, number or '150%'. |
y_scale | number | string | Keyframe[] | Expr | 1 | Vertical scale factor, number or '150%'. |
x_skew | number | Keyframe[] | Expr | 0 | Horizontal shear in degrees (CSS skewX); positive moves the bottom edge right. |
y_skew | number | Keyframe[] | Expr | 0 | Vertical shear in degrees (CSS skewY); positive moves the right edge down. |
Visual#
| Field | Type | Default | Description |
|---|---|---|---|
opacity | number | Keyframe[] | Expr | 1 | Element opacity, 0 (transparent) to 1 (opaque). Multiplies down through parent groups (group 0.5 × child 0.8 = 0.4 on screen). |
blend_mode | enum | 'normal' | How the element composites with what's beneath it. One of normal, multiply, screen, add, overlay, hard-light, soft-light (7 modes). Element-local — never affects other elements. See PROTOCOL.md §4.5. |
Filters#
Per-element color/blur filters. brightness/contrast/saturation are multipliers (not percentages): 1 = unchanged, 0 = removed, 2 = double.
| Field | Type | Default | Description |
|---|---|---|---|
blur_radius | number | Keyframe[] | Expr | 0 | Gaussian blur sigma in px applied to the element (0 = none). |
brightness | number | Keyframe[] | Expr | 1 | Brightness multiplier; 1 = unchanged, >1 brightens. |
contrast | number | Keyframe[] | Expr | 1 | Contrast multiplier around mid-gray; 1 = unchanged. |
saturation | number | Keyframe[] | Expr | 1 | Saturation multiplier; 1 = unchanged, 0 = grayscale. |
hue_rotate | number | Keyframe[] | Expr | 0 | Hue rotation in degrees. |
Effects & material#
| Field | Type | Default | Description |
|---|---|---|---|
effects | Effect[] | — | Stylize/keying effects applied in array order; they stack. Catalog (16 types): pixelate, dither, halftone, ascii, glass, backdrop_blur, pixel_shader, glow, drop_shadow, stroke, chroma_key, luma_key, levels, lut, fractal_noise, turbulent_displace. See PROTOCOL.md §4.7 for per-effect params. |
material | Material | — | PBR material (roughness, metalness, reflectivity, emissive, normal_map, normal_scale); only visible with scene lights/environment. See PROTOCOL.md §4.8. |
Animation#
animations#
Named animation presets. Declarative — each one is a tag the runtime expands into a keyframe sequence. AI agents and humans both pick from this list; you don't write the curves yourself.
interface Animation { type: AnimationType; "tok-cmt">// required duration?: number; easing?: EasingFunction; split?: "tok-str">'letter' | "tok-str">'word'; "tok-cmt">// text presets only stagger?: number; time?: "tok-str">'start' | "tok-str">'end' | number; frequency?: number; rotation?: number; distance?: number; direction?: "tok-str">'left' | "tok-str">'right' | "tok-str">'up' | "tok-str">'down'; scale?: number; seed?: number; axis?: "tok-str">'x' | "tok-str">'y' | "tok-str">'z'; }
| Field | Type | Default | Description |
|---|---|---|---|
type | AnimationType | required | The preset name. See list below. |
duration | number | 0.5 | Tween length in seconds (default 0.5 for most presets). |
easing | EasingFunction | preset-default (ease-out) | Easing curve for the tween. See easings. |
split | 'letter' | 'word' | — | (text presets only) Animate per 'letter' or per 'word'. |
stagger | number | ~0.09 word, ~0.035 letter | Delay between split units in seconds. |
time | 'start' | 'end' | number | 'start' | When the tween runs. 'start' = element appears; 'end' = element disappears; a number is a time in seconds within the element's duration. |
frequency | number | preset-default | Oscillation frequency in Hz, for oscillating presets like wiggle. |
rotation | number | preset-default | Rotation magnitude in degrees (preset-specific, e.g. spin 360). |
distance | number | preset-default | Travel distance in px (preset-specific, e.g. slide 40). |
direction | 'left' | 'right' | 'up' | 'down' | preset-default | Travel direction for slide/fly-style presets. |
scale | number | 0.3 | Squash/scale depth, 0–1. |
seed | number | 0 | Noise seed, non-negative integer. |
axis | 'x' | 'y' | 'z' | 'x' | For text-flip: the 3D rotation axis. |
Preset names#
All 31 presets:
fade-in, fade-out, slide-left-in, slide-right-in, slide-up-in, slide-down-in, slide-left-out, slide-right-out, slide-up-out, slide-down-out, scale-in, scale-out, rotate-in, rotate-out, bounce-in, bounce-out, spin, shake, wiggle, squash, pan, shift, drift, breathe, orbit, text-appear, text-slide, text-fly, text-typewriter, text-wave, text-flip.
Example#
"animations": [ { "type": "fade-in", "duration": 0.6 }, { "type": "slide-up-in", "duration": 0.6, "easing": "ease-out-cubic" }, { "type": "fade-out", "duration": 0.4, "time": "end" } ]
keyframe_animations#
Raw keyframe animation on any property. Use this when no preset fits — explicit position curves, color shifts, scale ramps tied to specific times.
interface KeyframeAnimation { property: string; keyframes: Keyframe[]; "tok-cmt">// at least one easing?: EasingFunction; auto_orient?: boolean; "tok-cmt">// "tok-str">'position' paths only loop?: boolean | "tok-str">'ping-pong'; } interface Keyframe { time: number | string; value: number | string | [number, number] | [number, number, number]; easing?: EasingFunction; }
| Field | Type | Default | Description |
|---|---|---|---|
property | string | required | Property to animate: 'x', 'y', 'opacity', 'rotation', 'scale', 'mask.progress', 'paths.0.stroke_progress', or 'position' for an [x,y]/[x,y,z] path. |
keyframes | Keyframe[] | required | At least one entry, in ascending time order. A Keyframe.value may be a number, a string (color/length), or a position [x,y] / [x,y,z]. |
easing | EasingFunction | — | Default easing for keyframes that don't set their own. |
auto_orient | boolean | false | On a 'position' path, rotate the element to face its travel direction. |
loop | boolean | 'ping-pong' | omit = clamp | Repeat the track: true (wrap), 'ping-pong' (reflect), or omit to clamp. |
Example#
"keyframe_animations": [ { "property": "y", "keyframes": [ { "time": 0, "value": 200, "easing": "ease-out-cubic" }, { "time": 1.5, "value": 540 } ] } ]
Expressions#
Any numeric field above (x, y, width, height, rotation, scale, opacity, blur/filter params, effect params) also accepts an expression object — a formula evaluated per frame, instead of a static value or a keyframe table:
{ "y": { "expr": "540 + sin(t * PI) * 30" } }
The formula is a pure function of the element's own clock: t (element-local seconds), dur, i (index in a generated set), n (siblings), value (the field's base default), the constants PI/TAU/E, and a fixed math function set (sin, cos, clamp, lerp, smoothstep, linear, ease, noise, wiggle, random, …). It cannot reference other elements or read runtime input, so it is deterministic and can be baked to keyframes. Full grammar: PROTOCOL.md §3.6.
Easing functions#
36 named easings plus 2 parametric forms.
linear, ease, ease-in, ease-out, ease-in-out,
ease-in-cubic, ease-out-cubic, ease-in-out-cubic,
ease-in-quad, ease-out-quad, ease-in-out-quad,
ease-in-quart, ease-out-quart, ease-in-out-quart,
ease-in-quint, ease-out-quint, ease-in-out-quint,
ease-in-sine, ease-out-sine, ease-in-out-sine,
ease-in-expo, ease-out-expo, ease-in-out-expo,
ease-in-circ, ease-out-circ, ease-in-out-circ,
ease-in-back, ease-out-back, ease-in-out-back,
spring (damped harmonic with mass=1, damping=10, stiffness=100 — overshoots ~5% then settles),
elastic-in, elastic-out, elastic-in-out,
bounce-in, bounce-out, bounce-in-out.
Two parametric forms are also accepted as strings:
cubic-bezier(x1, y1, x2, y2)— a custom Bézier curve.steps(n)—ndiscrete steps (positive integer).