Animation

Advanced animation system for creating dynamic, procedural, and expression-based animations beyond simple keyframes.

Animation Types

The animation system supports several types beyond traditional keyframes:

  • Expression: JavaScript expressions evaluated per frame
  • Procedural: Built-in procedural animation patterns
  • Sequence: Step-based sequential animations
  • Path: Motion along Bezier paths

These advanced animations are defined in the animations array property of elements.

Node Animations vs Keyframes

Node Animations (animations array):

  • Procedural, expression-based, or path-following animations
  • Evaluated every frame
  • Higher priority than keyframes
  • Can override keyframe values

Keyframes (keyframes array):

  • Simple property interpolation between defined points
  • Lower priority
  • Predictable, easy to edit

Animation Priority

When multiple animation methods target the same property:

  1. Highest: Node animations (expression, procedural, path, sequence)
  2. Medium: Keyframe animations
  3. Lowest: Static property values
PropTypeRequiredExampleValue RangeDescription
typestringtrue-expression | procedural | sequence | pathThe type of animation.
enabledbooleanfalse--Whether the animation is active.
targetstringtrue--The property path to animate (e.g., "x", "rotation", "scaleX").
startTimenumberfalse--Start time of the animation relative to element start, in seconds.
durationnumberfalse--Duration of the animation in seconds. For looping animations, this is the loop period.
loopbooleanfalse--Whether the animation loops.
blendModestringfalse-replace | add | multiplyHow this animation combines with others: replace (override), add (sum), multiply (scale).
blendWeightnumberfalse--The strength/influence of this animation (0-1). Used for blending multiple animations.

Expression Animations

Expressions are JavaScript code evaluated per frame, with access to animation context variables.

Available Context Variables

1{ 2 time: number; // Current absolute time (seconds) 3 localTime: number; // Time relative to element start 4 duration: number; // Element duration 5 progress: number; // Normalized progress (0-1) 6 index: number; // Element index in group 7 total: number; // Total elements in group 8 fps: number; // Frames per second 9 width: number; // Canvas width 10 height: number; // Canvas height 11 value: any; // Current property value 12}

Expression Example

Bounce animation using Math functions:

expressionAnimation.json
1{ 2"id": "element-1", 3"type": "Image", 4"start": 0, 5"duration": 5, 6"animations": [ 7 { 8 "type": "expression", 9 "target": "y", 10 "enabled": true, 11 "expression": "100 + Math.abs(Math.sin(localTime * 3)) * 200", 12 "startTime": 0, 13 "duration": 5, 14 "loop": true 15 } 16] 17} 18

Complex Expression

Combine multiple effects:

complexExpression.json
1{ 2"animations": [ 3 { 4 "type": "expression", 5 "target": "rotation", 6 "expression": "(localTime * 2 * Math.PI / duration) + Math.sin(localTime * 4) * 0.1", 7 "loop": true 8 }, 9 { 10 "type": "expression", 11 "target": "scaleX", 12 "expression": "1 + Math.sin(localTime * 5) * 0.2", 13 "loop": true 14 } 15] 16} 17

Procedural Animations

Built-in animation patterns with configurable parameters.

Procedural Types

  • wiggle: Random oscillation (useful for handheld camera effects)
  • wave: Sine/cosine wave motion
  • bounce: Elastic bounce effect
  • pulse: Rhythmic scaling

Wiggle Example

wiggleAnimation.json
1{ 2"animations": [ 3 { 4 "type": "procedural", 5 "proceduralType": "wiggle", 6 "target": "rotation", 7 "enabled": true, 8 "frequency": 2, 9 "amplitude": 0.1, 10 "loop": true 11 } 12] 13} 14

Sequence Animations

Step-based animations that change values at specific intervals.

PropTypeRequiredExampleValue RangeDescription
valuesarraytrue--Array of values to step through.
stepDurationnumbertrue--Duration of each step in seconds.
loopbooleanfalse--Whether to loop the sequence.

Sequence Example

Text cycling through different sizes:

sequenceAnimation.json
1{ 2"animations": [ 3 { 4 "type": "sequence", 5 "target": "fontSize", 6 "enabled": true, 7 "values": [24, 32, 40, 32, 24], 8 "stepDuration": 0.5, 9 "loop": true 10 } 11] 12} 13

Path Animations

Animate elements along Bezier curve paths.

PropTypeRequiredExampleValue RangeDescription
pathstring | arraytrue--SVG path data (d attribute) or array of path points.
autoRotatebooleanfalse--Whether to automatically rotate element to follow path tangent.
durationnumbertrue--Time to complete the full path.

Path Example

Circular motion:

pathAnimation.json
1{ 2"animations": [ 3 { 4 "type": "path", 5 "target": "position", 6 "enabled": true, 7 "path": "M 100,100 a 200,200 0 1,1 0,1 Z", 8 "autoRotate": true, 9 "duration": 4, 10 "loop": true 11 } 12] 13} 14

Property Path Syntax

The target property uses dot notation to specify nested properties:

  • Simple: "x", "y", "rotation"
  • Nested: "scale.x", "position.y"
  • Array: "colors[0]", "points[2].x"

Target Resolution Examples

1{ 2 "target": "x" // Element's x position 3 "target": "scaleX" // Horizontal scale 4 "target": "alpha" // Opacity 5 "target": "fill.color" // Fill color (if fill is object) 6 "target": "points[0].x" // First point's x coordinate 7}

Blend Modes

Control how animations combine when multiple target the same property:

Replace (Default)

1{ 2 "blendMode": "replace", 3 "blendWeight": 1.0 4}

Completely replaces the property value.

Add

1{ 2 "blendMode": "add", 3 "blendWeight": 0.5 4}

Adds to the existing value (weighted). Useful for accumulating offsets.

Multiply

1{ 2 "blendMode": "multiply", 3 "blendWeight": 1.0 4}

Multiplies the existing value. Useful for scaling effects.

Multiple Animations Example

Combining different animation types:

multipleAnimations.json
1{ 2"id": "animated-element", 3"type": "Shape", 4"animations": [ 5 { 6 "type": "expression", 7 "target": "x", 8 "expression": "width / 2 + Math.sin(localTime * 2) * 300", 9 "blendMode": "replace", 10 "loop": true 11 }, 12 { 13 "type": "procedural", 14 "proceduralType": "wiggle", 15 "target": "rotation", 16 "frequency": 3, 17 "amplitude": 0.2, 18 "blendMode": "add", 19 "blendWeight": 0.5 20 }, 21 { 22 "type": "sequence", 23 "target": "alpha", 24 "values": [1, 0.8, 1], 25 "stepDuration": 0.3, 26 "loop": true, 27 "blendMode": "multiply" 28 } 29], 30"keyframes": [ 31 { 32 "id": "kf-1", 33 "time": 0, 34 "stateObj": { "scaleX": 1 } 35 }, 36 { 37 "id": "kf-2", 38 "time": 2, 39 "stateObj": { "scaleX": 1.5 } 40 } 41] 42} 43

In this example:

  • Expression controls horizontal position (replace mode)
  • Procedural wiggle adds subtle rotation variation
  • Sequence pulses opacity
  • Keyframe scales the element (lowest priority)

Important Notes

Expression Sandbox

  • Expressions run in a restricted context for security
  • Access to animation context variables only
  • Cannot access DOM, external libraries, or Node.js APIs
  • Evaluation errors will disable the animation

Performance

  • Expressions: Evaluated every frame - keep them simple
  • Procedural: Optimized built-in functions - very efficient
  • Sequence: Minimal overhead, good for discrete changes
  • Path: Moderate cost, depends on path complexity

Timing

  • startTime and duration are relative to element's start time
  • localTime in expressions starts at 0 when the element begins
  • Animations respect element's overall duration boundary

Debugging

  • If an animation doesn't work, check:
    • enabled: true
    • target property exists on the element
    • Expression syntax is valid JavaScript
    • startTime is within element's duration
    • No property path typos

Common Use Cases

Smooth Following Effect

1{ 2 "type": "expression", 3 "target": "x", 4 "expression": "value + (targetX - value) * 0.1" 5}

Elastic Bounce

1{ 2 "type": "expression", 3 "target": "scaleY", 4 "expression": "1 + Math.exp(-localTime * 3) * Math.cos(localTime * 8) * 0.5" 5}

Camera Shake

1{ 2 "type": "procedural", 3 "proceduralType": "wiggle", 4 "target": "x", 5 "frequency": 10, 6 "amplitude": 5 7}

Staggered Animation (in groups)

1{ 2 "type": "expression", 3 "target": "alpha", 4 "expression": "Math.max(0, Math.min(1, (localTime - index * 0.1) * 2))" 5}