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

{
  time: number; // Current absolute time (seconds)
  localTime: number; // Time relative to element start
  duration: number; // Element duration
  progress: number; // Normalized progress (0-1)
  index: number; // Element index in group
  total: number; // Total elements in group
  fps: number; // Frames per second
  width: number; // Canvas width
  height: number; // Canvas height
  value: any; // Current property value
}

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

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

Blend Modes

Control how animations combine when multiple target the same property:

Replace (Default)

{
  "blendMode": "replace",
  "blendWeight": 1.0
}

Completely replaces the property value.

Add

{
  "blendMode": "add",
  "blendWeight": 0.5
}

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

Multiply

{
  "blendMode": "multiply",
  "blendWeight": 1.0
}

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

{
  "type": "expression",
  "target": "x",
  "expression": "value + (targetX - value) * 0.1"
}

Elastic Bounce

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

Camera Shake

{
  "type": "procedural",
  "proceduralType": "wiggle",
  "target": "x",
  "frequency": 10,
  "amplitude": 5
}

Staggered Animation (in groups)

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