View raw

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#

FieldTypeDefaultDescription
idstringautoStable identifier. Used by the MCP tools edit_element / delete_element and by keyframe_animations.property references. If omitted, the runtime assigns one.
namestringOptional human-readable label. Has no rendering effect.
typeElementTyperequiredOne of 'video', 'image', 'text', 'shape', 'audio', 'group', 'caption', 'particles'. Discriminates which variant fields apply.

Timing#

FieldTypeDefaultDescription
layernumberrequiredThe 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.
visiblebooleantrueWhen false, the element is skipped entirely (not rendered, no audio).
timenumber | string0When the element appears, in seconds (or "50%" of the Source duration).
durationnumber | 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).

FieldTypeDefaultDescription
repeatnumberRender 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_datarow[]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_staggernumber0Seconds 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.
stylestringName 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.

FieldTypeDefaultDescription
xnumber | string | Keyframe[] | Expr0Horizontal position of the anchor. Number = pixels; string = '50%', '100vw', etc. With the default anchor (left), this is the box left edge.
ynumber | string | Keyframe[] | Expr0Vertical position of the anchor. With the default anchor (top), this is the box top edge.
x_anchornumber | string0 ('0%')Anchor offset within the element's box. '0%' = left edge (default), '50%' = center, '100%' = right edge.
y_anchornumber | string0 ('0%')Anchor offset within the element's box. '0%' = top (default), '50%' = center, '100%' = bottom.
widthnumber | string | Keyframe[] | Exprcontent-derivedWidth of the element's box in px or a string like '50%'/'100vw'.
heightnumber | string | Keyframe[] | Exprcontent-derivedHeight of the element's box in px or a string like '50%'/'100vh'.
aspect_rationumberOptional fixed width-to-height ratio.
rotationnumber | Keyframe[] | Expr0In-plane rotation around the box center, in degrees.
scalenumber | Keyframe[] | Expr1Uniform 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.

FieldTypeDefaultDescription
znumber | Keyframe[] | Expr0Depth 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_rotationnumber | Keyframe[] | Expr0Same slot as rotation (in-plane degrees about the box center). Author one, not both — supplying both is a validation error.
x_rotationnumber | Keyframe[] | Expr0Rotation about the local x axis in degrees; tips the top edge away under a camera.
y_rotationnumber | Keyframe[] | Expr0Rotation about the local y axis in degrees; turns the right edge away under a camera.
x_scalenumber | string | Keyframe[] | Expr1Horizontal scale factor, number or '150%'.
y_scalenumber | string | Keyframe[] | Expr1Vertical scale factor, number or '150%'.
x_skewnumber | Keyframe[] | Expr0Horizontal shear in degrees (CSS skewX); positive moves the bottom edge right.
y_skewnumber | Keyframe[] | Expr0Vertical shear in degrees (CSS skewY); positive moves the right edge down.

Visual#

FieldTypeDefaultDescription
opacitynumber | Keyframe[] | Expr1Element opacity, 0 (transparent) to 1 (opaque). Multiplies down through parent groups (group 0.5 × child 0.8 = 0.4 on screen).
blend_modeenum'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.

FieldTypeDefaultDescription
blur_radiusnumber | Keyframe[] | Expr0Gaussian blur sigma in px applied to the element (0 = none).
brightnessnumber | Keyframe[] | Expr1Brightness multiplier; 1 = unchanged, >1 brightens.
contrastnumber | Keyframe[] | Expr1Contrast multiplier around mid-gray; 1 = unchanged.
saturationnumber | Keyframe[] | Expr1Saturation multiplier; 1 = unchanged, 0 = grayscale.
hue_rotatenumber | Keyframe[] | Expr0Hue rotation in degrees.

Effects & material#

FieldTypeDefaultDescription
effectsEffect[]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.
materialMaterialPBR 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';
}
FieldTypeDefaultDescription
typeAnimationTyperequiredThe preset name. See list below.
durationnumber0.5Tween length in seconds (default 0.5 for most presets).
easingEasingFunctionpreset-default (ease-out)Easing curve for the tween. See easings.
split'letter' | 'word'(text presets only) Animate per 'letter' or per 'word'.
staggernumber~0.09 word, ~0.035 letterDelay 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.
frequencynumberpreset-defaultOscillation frequency in Hz, for oscillating presets like wiggle.
rotationnumberpreset-defaultRotation magnitude in degrees (preset-specific, e.g. spin 360).
distancenumberpreset-defaultTravel distance in px (preset-specific, e.g. slide 40).
direction'left' | 'right' | 'up' | 'down'preset-defaultTravel direction for slide/fly-style presets.
scalenumber0.3Squash/scale depth, 01.
seednumber0Noise 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;
}
FieldTypeDefaultDescription
propertystringrequiredProperty to animate: 'x', 'y', 'opacity', 'rotation', 'scale', 'mask.progress', 'paths.0.stroke_progress', or 'position' for an [x,y]/[x,y,z] path.
keyframesKeyframe[]requiredAt 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].
easingEasingFunctionDefault easing for keyframes that don't set their own.
auto_orientbooleanfalseOn a 'position' path, rotate the element to face its travel direction.
loopboolean | 'ping-pong'omit = clampRepeat 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)n discrete steps (positive integer).