Docs

Animations

Animations make things move. They can live on blueprints, instances, and parametric define bodies. Blueprint animations are inherited by instances; instance animations are added on top and can target the same property.

From/To Syntax

The most common form: animate a property from one value to another, over a duration.

imagine card
  animate position.x from -80 to 0 over 0.4s ease "ease-out" delay 0.1s

create card

ease, delay, repeat, and direction are retained when written inline. speed, blend, weight, and disabled are reliably represented as indented options. The same animation can be written as an indented property block instead of the inline form:

imagine card
  animate opacity
    from: 0
    to: 1
    duration: 0.4s
    ease: "ease-out"

create card

Percent keyframes animate a relative size or position: values interpolate in percent space and resolve against the parent each frame, so they track a parent that resizes mid-animation. A transition that mixes units on an axis, such as from 100 to 50%, resolves both keyframes against the parent and interpolates smoothly.

imagine dot
  shape: circle
  size: (10%, 10%)
  animate size from (10%, 10%) to (80%, 80%) over 1s

create dot

Multi-property Animations

Use one animate block when several properties describe one transition. The optional alias names the behavior; every property shares one duration, easing, delay, repeat count, playback direction, speed, disabled state, selector, and stagger.

imagine title
  text: "Hello world"
  animate as riseFade
    selector: all words
    stagger: 0.08s
    duration: 0.5s
    ease: "ease-out"
    from:
      opacity: 0
      position.y: 24
    to:
      opacity: 1
      position.y: 0

create title

The from and to maps must contain the same property paths. Omit the as alias when the behavior does not need a name. Child properties cannot override timing; use another animate block when a channel needs different timing.

For text transitions, the selector is resolved once for the block. Every child property then samples the same selected units with the same stagger offsets.

Grouped transforms use transition-friendly composition: position and rotation are additive offsets, while scale and opacity multiply the authored base value. Other properties use normal replacement. This lets the example rise relative to its authored position and fade to its authored opacity.

Keyframes

For multi-stop motion, list times and values:

imagine ball
  animate position.y
    0s: 0
    0.3s: 120 ease "ease-out"
    0.6s: 0 ease "ease-in"
    repeat: infinite
    direction: alternate

create ball

Both 0.3s: value and at 0.3s: value are accepted. Named easing values are defined by EasingType; cubic bezier easing uses a four-number tuple.

An ease belongs to the outgoing segment: the ease at 0s controls motion from 0s to the next key. The final key's ease has no following segment. Boolean keys switch at the next key's exact time. Their switch is independent of easing, including hold, back and elastic curves. Numeric values retain their selected interpolation. Camera active uses these ordinary boolean tracks; camera pose transitions also use this animation engine and its existing blend modes.

Named easing presets include expo, quint, back, bounce and elastic variants. Back and elastic can overshoot.

A cubic bezier ease shapes timing. By default, on a numeric 2D position, it also bends the path between keys. Add interpolation: timing to keep the path straight, or interpolation: spatial to request that existing curved behavior. Omitting the option preserves old scenes. Components such as position.x, 3D or relative-length tuples, and every non-position tuple (size, start, end, effect offsets) always use timing only. Spatial control does not turn an arbitrary tuple into a motion path.

imagine dot
  shape: circle
  animate position from (-100, -50) to (100, 50) over 1s
    ease: (0.42, 0, 0.58, 1)
    interpolation: timing

create dot

Constant defines, including aliases and tuple components, resolve before key interpolation in root, parent and local scope. Local names win. The saved keys retain their references. Expression-valued defines and formulas inside key values are not evaluated by this constant-key path; use animate property: for formulas. Unresolved or cyclic names stay unresolved, never an invented numeric zero. Parametric define arguments continue to substitute before playback. Keyframed text animations accept the same selector and stagger options as from/to animations; filtering does not renumber each.index.

See examples for scenes you can copy and edit.

imagine ball
  animate position.y
    at 0s: 0
    at 0.3s: 120 ease "ease-out"

create ball

One Component of a Tuple

Add .x, .y, or .z to a tuple property to animate one component and keep the others. This works on transforms and on any other tuple, such as a line's start and end. Here a line draws itself from left to right:

imagine underline
  shape: line
  start: (0, 0)
  end: (0, 0)
  stroke: "#ffffff"
  strokeWidth: 4
  animate end.x from 0 to 300 over 0.5s ease "ease-out"

create underline with
  position: (-150, -40)

A component animation wins over an animation of the whole tuple on the same axis. When the tuple isn't set, the component starts from its documented default, so end starts from (100, 0).

Expression Animations

An animation with a colon right after the property path is evaluated every frame. See expressions for the available expression scope.

imagine wiggler
  animate position.x: sin(time * 2) * 30
    blend: add

create wiggler

Per-letter / word / line animation

On a text object, an animation can target individual graphemes, words, or lines with a selector, and stagger each unit in time with stagger:

imagine title
  text: "Hello world"
  animate opacity
    from: 0
    to: 1
    duration: 0.5s
    selector: all
    stagger: 0.05s

create title

Selector values read like prose — a pattern and a unit, or a unit and an index/range:

Value Targets
all / even / odd / first / last Grapheme pattern.
letters / words / lines Every unit of that kind.
all words · even words · odd words · first word · last word Word pattern.
all lines · even lines · odd lines · first line · last line Line pattern.
3 · letter 3 · word 2 · line 0 One unit by index.
"2-5" · letters 2-5 · words 1-3 · lines 0-2 Unit range (inclusive).

Unit words are letter/letters, word/words, line/lines; the pattern and unit may appear in either order (even words or words even). A bare pattern, index, or range means letters — the common case.

Selectors address graphemes, not raw characters — combining marks belong to their base letter, and spacing stays addressable. A word is a run containing letters or digits: punctuation is never a word ("Hello, world!" has two words). An apostrophe between letters or digits stays inside the word (don't, it’s), and so does a . or , between digits (3.14, 1,000). A line is a laid-out line, so wrapped text has one line per visual row. Empty lines hold no units and are skipped: in "A\n\nB", line 1 is B. stagger is the per-unit delay in seconds (or a parameter reference): letters advance one at a time, words and lines advance as whole units.

On text, a property or animation expression that reads each.index, each.count, each.progress, or each.seed implicitly targets all letters. The common per-letter gradient can be written directly on the property:

imagine title
  text: "Gradient"
  fill: mix("#ff0000", "#0000ff", each.progress)

create title

The same expression works in animate fill:. Add an explicit selector only to target another unit or subset, such as even, all words, or line 0.

each.index is the stable zero-based index in the complete grapheme, word, or line domain, and each.count is that complete domain's size. each.progress is each.index / (each.count - 1), or 0 for one unit. A selector filters the domain without renumbering these values; only stagger follows selected order. each.seed is a hashed seed for random(each.seed); it is stable for the same domain index.

Text selectors animate position, rotation, scale, opacity, and color channels. Position, rotation, and scale support .x, .y, and .z component paths; full tuple values animate all supplied axes. Scalar rotation is the z axis and scalar scale applies uniformly to x/y/z. This is the same transform model used by whole objects, so per-letter XYZ motion composes with parent transforms. Percentage position offsets resolve against the laid-out text block's width and height; percentage opacity resolves as a fraction of 1. Other selected properties are ignored rather than changing an unrelated glyph channel.

Animating fill (or color) through a selector sets a per-glyph color. By default it replaces the text's paint for the selected glyphs; with blend: multiply it tints the existing paint instead.

Moving a sequence

Use one scene timeline. Share a start value across animation delays and visibility:

define start: 2
define arrive(at: 0s):
  animate position.x from -100 to 0 over 0.5s delay at ease "ease-out"
duration: 5
imagine title
  text: "One timeline."
  fontFamily: "Inter"
create title as headline
  visible: time >= start && time < start + 2
  arrive(start)

Change start to move the entrance and its visibility together. delay accepts static numeric defines and aliases, with the same scope as key values. It shifts keyframe playback; it does not reset time in expressions. Write time - start when a formula needs an elapsed value. Nested groups share the scene timeline; a hidden parent hides its descendants, and overlapping groups keep paint order.

visible and camera active keys or expressions switch at exact scene times, even when artwork uses reduced FPS. Other motion keeps its existing FPS and per-unit stagger sampling. Set root duration for a finished film, especially when visibility or repeating expressions determine its end: arbitrary formulas cannot provide a finite inferred duration.

Shared modifiers and repeated type

Text units and copies share selection, stagger, expression, and animation evaluation. Add ordered modifier blocks to a text blueprint, or repeat the blueprint with for. See replication.