Animation API

Keyframes, timelines, blending, and IK — the complete animation reference.

Try it

title: "Keyframe oscillation"
description: "A shape bouncing between two positions with easing"
nodes:
  - type: ELLIPSE, name: "Ball", x: 200, y: 200, w: 50, h: 50, fill: "#E74C3C"
formulas:
  - node: "Ball", prop: "y", value: "200 + Math.sin(time * 3) * 100"
  - node: "Ball", prop: "x", value: "200 + Math.cos(time * 2) * 80"
tools_visible: [select, play]
ui_visible: [properties, formulas]

Animation clips

An animation clip is a named collection of per-node timelines.

AnimationClip {
  name: string                           // "Walk", "Jump", "Idle"
  timelines: Map<nodeId, BoneTimeline>   // one timeline per animated node
}

BoneTimeline {
  keyframes: FormulaKeyframe[]           // sorted by time
  loop: boolean                          // wrap time modular to last keyframe
}

Each node (bone) has its own timeline with independent loop length.

Formula keyframes

Keyframes hold change data — from/to values with easing:

FormulaKeyframe {
  t: number                // time of keyframe
  tIn: number              // blend-in duration before keyframe
  tOut: number             // blend-out duration after keyframe
  change: {
    [property]: {
      from: number         // value at start of segment
      to: number           // value at end of segment
      ease: EasingFunction // transition curve
    }
  }
}

Keyframes can also hold formula expressions instead of static from/to values. The formula evaluates at each frame within the keyframe segment.

Evaluation

Sequential seek with hint

Keyframes are sorted by time. The evaluator caches _lastIndex per timeline:

  • Forward playback: O(1) — advance from last known index
  • Random seek: O(n) worst case, binary search fallback

Normalized time

Within a segment (between two keyframes), t is normalized 0-1:

t = (currentTime - startKeyframe.t) / (endKeyframe.t - startKeyframe.t)

The easing function transforms this:

easedT = ease(t) // e.g., easeInOut(0.5) ≈ 0.5
value = from + (to - from) * easedT

Easing functions

NameBehavior
linearConstant speed
easeInSlow start
easeOutSlow end
easeInOutSlow both ends
cubicBezier(x1, y1, x2, y2)Custom curve
spring(mass, stiffness, damping)Physics-based overshoot
steps(count)Discrete jumps
elasticBouncy overshoot
bounceLanding impact

Per-property easing — rotation bounces while position eases smoothly in the same keyframe.

Blending

Mock objects

Animations evaluate into preallocated mock objects, not directly into nodes:

AnimationMock {
  x, y, rotation, width, height, opacity
}

Two mocks per bone. Animation A → mockA. Animation B → mockB. Blend by weight, apply result to real node.

Blend modes

Freeze-blend: Source freezes at current pose, target advances. Weight ramps from source to target.

playAnimation("Jump", { duration: 0.3, mode: "freeze" })

Live-blend: Both animations keep playing, weight shifts gradually.

playAnimation("Run", { duration: 0.5, mode: "live", sourceSpeed: 0.5 })

State capture

Current node state is captured when playAnimation() is called. The snapshot becomes the blend source. Chained transitions (Walk → Jump → Land) capture mid-blend poses naturally.

Playback API

// Play a clip
playAnimation(clipName: string, blend?: BlendConfig)

// Stop all animation
stopAnimation()

// BlendConfig
{
  duration: number        // crossfade time in seconds
  mode: "freeze" | "live" // blend mode
  sourceSpeed?: number    // during live blend, slow source
}

IK solvers

IK runs after animation blending, before applying to nodes.

Two-bone (analytical)

For arms and legs. Uses law of cosines — exact answer in one frame.

CCD (Cyclic Coordinate Descent)

Iterative, any chain length. 5-15 iterations. Good for tails, tentacles.

FABRIK

Iterative, position-based. Handles constraints well. Good for spines, ropes.

IK chain definition

IKChain {
  bones: string[]              // nodeIds root → tip
  targetSlot: string           // node to reach
  poleSlot?: string            // bend direction hint
  solver: "twoBone" | "ccd" | "fabrik"
  weight: number               // 0-1 blend with keyframed pose
  constraints: JointConstraint[]
}

JointConstraint {
  nodeId: string
  minAngle: number             // radians
  maxAngle: number             // radians
  preferredAngle?: number      // rest pose bias
}

IK weight is animatable — keyframe it for transitions between keyframed and procedural motion.

Component slots

Slots are typed VNode inputs on components. IK targets, look-at targets, and other spatial references use slots:

// In formula: reference a slot
EyeRotation = atan2($lookAtTarget.y - this.y, $lookAtTarget.x - this.x)

During authoring, slots show placeholder nodes. At runtime, they bind to real scene nodes.

Export

FormatSupports
Standalone JSFull: keyframes, formulas, blending, IK
Lottie JSONPartial: static keyframes only (no formulas, no IK)
CSS @keyframesSimple: single-property animations

Integration with formulas

Priority order:

  1. Animation keyframe active → animation value
  2. Formula bound → formula evaluates
  3. Neither → static value

Formulas can reference time and frame for procedural animation. Keyframes can hold formulas. The two systems compose.


Formulas APIPhysics APINodes

← НазадComponents APIДальше →Documentation