Timeline motion design

This guide describes the timeline document fields available to composition authors. These are document examples. The editor does not expose controls for every field. The protocol schema and renderer are the source of truth when a field is absent here.

Easing and animation

Animation easing accepts named curves, cubic-bezier(x1,y1,x2,y2), and spring(stiffness,damping,mass). Named curves include linear, easeIn, easeOut, easeInOut, easeOutBack, easeOutElastic, easeOutBounce, easeInExpo, easeOutExpo, easeInOutExpo, easeInQuint, easeOutQuint, easeInOutQuint, easeInCirc, easeOutCirc, easeInOutCirc, and hold. Unknown easing strings resolve to linear. Easing may be set on an animation or on an individual keyframe segment.

{
  "role": "in",
  "preset": "slide",
  "durationMs": 700,
  "easing": "cubic-bezier(0.16,1,0.3,1)",
  "params": { "direction": "up", "distance": 0.12 }
}

spring(...) is an easing curve for an authored animation segment, not a physical simulation that responds to changing forces. The animation engine’s numeric curves drive transform, opacity, blur, grade, wipe, shape trim, and 3D model camera channels. For arbitrary visual properties use styleTracks:

{
  "role": "in",
  "preset": "fade",
  "durationMs": 600,
  "styleTracks": [
    { "target": "text.color", "keyframes": [
      { "t": 0, "value": "#8bd5ff" },
      { "t": 1, "value": "#ffffff", "easing": "easeOutExpo" }
    ] },
    { "target": "effect.glow-1.intensity", "keyframes": [
      { "t": 0, "value": 0 }, { "t": 1, "value": 1.2 }
    ] },
    { "target": "effect.bg-field.colorB", "keyframes": [
      { "t": 0, "value": "#1e1b4b" }, { "t": 1, "value": "#0f3b3a" }
    ] }
  ]
}

Targets include supported numeric shape and text style fields, including font size, weight, and spacing, shape/text colors, gradient fill angles and stop values, effect.<effect-id>.<numeric-field>, supported effect color fields, mask bounds and feather, compatible shape.d / mask.d SVG paths, clip.borderRadius, mask.radiusPx, and per-glyph color, blur, or tracking. Color keyframes accept the CSS colors supported by the shared parser, including named colors, hex, RGB, and HSL. A clip must have the matching style, mask, or effect. Path morph endpoints must have the same SVG command and point count structure. Glyph tracks require a text stagger. Links are the supported cross-property mechanism. Arbitrary expressions are not evaluated at render time. Effect color targets are generator.colorA, generator.colorB, and color on dropShadow, chromaKey, or the color-using stylize modes (lightRays, lensFlare, innerShadow, innerGlow, edgeHighlight, and lightLeakOverlay), addressed through each effect’s ID. The targeted color field must already be present. String fields such as an effect’s mode and the currently unused glow.color cannot be animated by a style track.

Text content can count or decode over the animation window:

{
  "role": "in", "preset": "fade", "durationMs": 900,
  "textAnimator": { "kind": "ticker", "from": 0, "to": 2847,
    "groupSeparator": "," }
}

Ticker also accepts decimals, padTo, prefix, and suffix. groupSeparator inserts the given 1–4 character string between groups of three integer digits, after zero padding. Without it, existing tickers remain ungrouped. A deterministic scramble can be authored with {"kind":"scramble","charset":"ABC123","seed":17}. Both operate on a text clip’s content during the animation. Per-unit transform and opacity animation supports character, word, or wrapped-line stagger, with order start, end, or center:

"stagger": { "unit": "word", "offsetMs": 90, "from": "center" }

Unknown stagger units fall back to block animation. Existing text path support sets textStyle.path to an SVG path in normalized canvas coordinates. The text baseline follows that path. It is not a new per-character motion-path animator. The followPath animation preset moves a clip along its authored path parameters.

An animation can start on a beat by storing beat on the animation. index is one-based. scope: "clip" counts from the clip start. scope: "sequence" counts from the sequence tempo’s offsetMs. offsetMs shifts the selected beat, and delayMs remains an additional animation delay. Beat spacing uses the sequence BPM (or the default tempo if none is set).

{
  "role": "emphasis", "preset": "pop", "durationMs": 240,
  "beat": { "index": 3, "scope": "clip", "offsetMs": -20 }
}

To stagger selected clips in a visual order without moving their media, call ui_timeline_stagger_animations with:

{
  "clip_ids": ["logo", "wordmark", "tagline"],
  "offset_ms": 120
}

List clip IDs, not names. Every selected clip must already have an animation. The first listed clip keeps its animation delays. Each later clip adds its index multiplied by non-negative offset_ms to each animation’s delayMs.

Links let a clip read a transform or opacity channel from another clip, with optional clock offset, looping, scale, and offset. A deterministic wiggle is also available. Links read the source’s authored animation and do not chain through other links, so cyclic dependencies are not evaluated.

{
  "animationLinks": [
    { "target": "positionX", "sourceClipId": "camera-card",
      "source": "positionX", "timeOffsetMs": 0, "scale": 0.35 },
    { "target": "rotation", "kind": "wiggle", "amplitude": 0.025,
      "frequencyHz": 1.4, "seed": 8 }
  ]
}

Channels are positionX, positionY, scale, rotation, and opacity. Links are per clip and support up to 32 entries in the document schema.

Space, layout, and timing

camera2d is optional on the sequence document, persists through timeline load, autosave, update, and document versioning, and may be set to null to clear it. Layers with transform.depthPx participate in camera perspective. The camera position and depth can be keyframed on absolute timeline times. focusDepthPx and aperturePx set a shallow depth-of-field blur.

{
  "camera2d": {
    "position": { "x": 0, "y": 0 }, "depthPx": 0,
    "focalLengthPx": 1400, "focusDepthPx": 0, "aperturePx": 18,
    "keyframes": [
      { "timeMs": 0, "position": { "x": 0, "y": 0 }, "depthPx": 0 },
      { "timeMs": 1200, "position": { "x": 90, "y": -20 }, "depthPx": 120 }
    ]
  }
}

Camera keyframes interpolate linearly. rotationX, rotationY, and perspective on a clip transform provide flat-layer tilt. Layout supports row or stack containers listing child clip IDs, and relative placement against another clip’s box. fitText sizes the relative clip to text plus padding. Boxes are resolved from current text or shape content at render time.

{
  "layout": { "kind": "row", "children": ["logo", "wordmark"], "gapPx": 24 }
}

Repeater copies advance by position and time, with an optional hue and brightness step. count includes the original clip. The renderer caps copies at 128. A repeater copy is a regular rendered clip with its own generated ID.

"repeater": { "count": 6, "positionStep": { "x": 180, "y": 0 },
  "timeStepMs": 67, "colorStep": { "hueDegrees": 8, "brightness": -0.02 } }

Motion blur may be configured per clip with samplesPerFrame (1–32) and shutterAngle (0–360 degrees). Preview and export evaluate each clip at its own shutter sample times, so a clip can blur independently of other layers. A value of 1 disables blur for that clip. Higher values set the minimum sample count. Blurred clips share the scene’s highest requested count, with evenly weighted samples across each clip’s own shutter window. temporalEcho adds up to 32 delayed copies. steppedTime quantizes that clip’s sampling clock.

Effects and transitions

The effect schema includes color grade, blur, glow, drop shadow, vignette, sharpen, chroma key, curves, levels, lift/gamma/gain, grain, pixelate, posterize, directional blur, lens distortion, LUTs, stylize modes, and generators. Stylize modes include RGB split, angular radial blur, zoom blur, displacement, turbulence, glitch, halftone, optional dither, light rays, lens flare, inner shadow/glow, and edge highlight. Generator modes include noise, fractal, conic and mesh gradients, animated gradient fields, particles, light leak, and grid pattern. A compact example:

"effects": [
  { "id": "grain-1", "type": "grain", "enabled": true,
    "amount": 0.12, "size": 1.1, "seed": 4 },
  { "id": "bg-field", "type": "generator", "enabled": true,
    "mode": "gradientField", "scale": 6, "time": 1.2,
    "animate": true, "seed": 9, "colorA": "#10233c", "colorB": "#ec6b45" },
  { "id": "grade-lut", "type": "lut", "enabled": true,
    "intensity": 0.8,
    "cube": "LUT_3D_SIZE 2\n0 0 0\n1 0 0\n0 1 0\n1 1 0\n0 0 1\n1 0 1\n0 1 1\n1 1 1" }
]

An effect must use the fields in its effect type. For example, blur uses radius, glow uses radius and intensity, and generator accepts mode, scale, time, seed, and color fields. .cube LUTs accept 3D tables. 1D tables are rejected. Store the file contents in cube through the document or effect tools. The editor does not yet provide a LUT file picker. The Canvas compositor implements the effect chain through CPU pixel passes. Hosts must supply scratch surfaces. Missing surfaces and unknown effect types are reported as rendering limitations.

Compositing blend functions operate on the stored encoded-sRGB channel values. The blend calculation does not linearize to light before combining layers. The compositor retains premultiplied alpha for the composite step. For dark gradients, an optional stylize effect with mode: "dither", amount: 1, and a fixed seed adds reproducible noise around the stored 8-bit values. This reduces visible banding but does not recover precision already lost in the source or guarantee that a video encoder preserves it.

Transitions live on the incoming clip as transitionIn, with durationMs. On a group clip, the transition applies to one precomposed surface containing its children. Keep children parented to the group on their own tracks. An overlapping previous sibling on the same track and under the same parent is the outgoing partner; without one, the group enters over the existing frame. Supported types are crossfade, dipToColor, wipe, push, slide, zoom, whip, zoomBlur, glitch, gradientWipe, iris, and lightLeak. Type-specific fields include color for dipToColor and lightLeak, direction for wipe, push, slide, whip, and gradient wipe, and softness for wipe, gradient wipe, and iris. gradientWipe thresholds a spatial field selected by map: "linear" | "radial" | "noise". scale sets noise frequency and seed fixes its pattern. lightLeak overlays a localized color field rather than covering the frame with a solid. Its scale sets falloff and seed shifts the field. Easing uses the same grammar as animation easing.

"transitionIn": { "type": "gradientWipe", "durationMs": 420,
  "direction": "left", "map": "noise", "scale": 8, "seed": 7,
  "softness": 0.12, "easing": "easeInOutExpo" }

Composition examples

The repository includes stored composition examples for title-slam, word-cards, window-frame, number-ticker, lower-third, and logo-sting. See the example compositions. They are starting documents, not a guarantee that every editor surface exposes each authoring field as a control.