Common element fields#
Every element type — text, shape, image, video, audio, composition, 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; track?: number; time?: number | string; duration?: number | string | 'auto' | 'end'; // Transform x?: number | string | Keyframe[]; y?: number | string | Keyframe[]; x_anchor?: number | string; y_anchor?: number | string; width?: number | string | Keyframe[]; height?: number | string | Keyframe[]; rotation?: number | Keyframe[]; scale?: number | Keyframe[]; // Visual opacity?: number | Keyframe[]; blend_mode?: 'normal' | 'multiply' | 'screen' | 'add' | 'overlay' | 'hard-light' | 'soft-light'; // Animation animations?: Animation[]; keyframe_animations?: KeyframeAnimation[]; }
Identity#
| Field | Type | Default | Description |
|---|---|---|---|
id | string | auto | Stable identifier. Used by modify_element / remove_element in the MCP server 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', 'composition', 'caption', 'particles'. Discriminates which variant fields apply. |
Timing#
| Field | Type | Default | Description |
|---|---|---|---|
track | number | 1 | Base draw order. Higher renders on top; track 1 is the bottom. It is the default and the tiebreak — the depth axis z (see Transform) takes precedence and orders elements; track decides only when depths are equal. Elements sharing a track keep document order. |
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. |
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[] | 0 | Horizontal position of the anchor. Number = pixels; string = '50%', '100vw', etc. |
y | number | string | Keyframe[] | 0 | Vertical position of the anchor. |
z | number | Keyframe[] | 0 | Depth toward (+) / away from (−) the viewer — the layering axis: higher z draws on top, with track as the tiebreak when depths are equal. Under a camera it also foreshortens. (See PROTOCOL §4.2 / §4.4 for the full 3D set: x_rotation, y_rotation, z_rotation.) |
x_anchor | number | string | '0%' | Anchor offset within the element's box. '0%' = left edge (default), '50%' = center, '100%' = right edge. |
y_anchor | number | string | '0%' | Anchor offset within the element's box. '0%' = top (default), '50%' = center, '100%' = bottom. |
width | number | string | Keyframe[] | content-derived | Width of the element's box in pixels. |
height | number | string | Keyframe[] | content-derived | Height of the element's box. |
rotation | number | Keyframe[] | 0 | Rotation around the element's center, in degrees. |
scale | number | Keyframe[] | 1 | Uniform scale around the element's center. Multiplies width and height. |
Visual#
| Field | Type | Default | Description |
|---|---|---|---|
opacity | number | Keyframe[] | 100 | Element opacity, 0–100. Multiplied with parent composition opacity. |
blend_mode | string | 'normal' | How the element composites with what's beneath it: normal, multiply, screen, add, overlay, hard-light, soft-light. Element-local — never affects other elements. See PROTOCOL.md §4.5. |
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; duration?: number; easing?: EasingFunction; time?: 'start' | 'end' | number; fade?: boolean; scope?: 'element' | 'track'; split?: boolean; }
| Field | Type | Default | Description |
|---|---|---|---|
type | AnimationType | required | The preset name. See list below. |
duration | number | 0.8 | How long the animation runs, in seconds. |
easing | EasingFunction | preset-default | Optional override. See easings. |
time | 'start' | 'end' | number | 'start' | When the animation fires. 'start' = element appears; 'end' = element disappears; a number is the absolute time within the element's duration. |
fade | boolean | preset-default | Whether the animation should also fade. Used by some presets like slide-*-in. |
scope | 'element' | 'track' | 'element' | Whether the animation applies to this element only or the whole track it sits on. |
split | boolean | false | (text/caption only) Animate per-character instead of the whole element. |
Preset names#
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.
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[]; easing?: EasingFunction; } interface Keyframe { time: number | string; value: number | string | [number, number]; easing?: EasingFunction; }
| Field | Type | Description |
|---|---|---|
property | string | Path to the property: 'x', 'y', 'opacity', 'rotation', 'mask.progress', 'paths.0.stroke_progress'. |
keyframes | Keyframe[] | At least two entries. Times must be ascending. |
easing | EasingFunction | Default easing between keyframes that don't specify their own. |
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#
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).