LeCodesdocs

animate

A one-shot value tween driven by the host's frame clock: interpolates from → to over duration and calls onUpdate with the current value every frame. It animates values, not objects — you apply the value yourself in onUpdate. For open-ended per-frame motion use setLoop (globals.md) instead.

At a glance

TypeScript
const id = animate({
  from: 0,
  to: 1,
  duration: 400,                     // milliseconds
  easing: easeOut,
  onUpdate: v => { ghost.opacity = v },
  onComplete: () => console.log('faded in'),
})

Starting a tween

TypeScript
animate({
  from: T,                  // start value
  to: T,                    // end value — its shape picks the interpolation (see below)
  duration: number,         // MILLISECONDS
  onUpdate(val: T): void,   // called every frame with the interpolated value
  onComplete?(): void,      // called once, after the final onUpdate
  easing?: Easing,          // maps linear 0..1 progress; default linear
}): number                  // animation id for stop/pause/resume
Note

duration is milliseconds (frame dt is seconds; UI .animateTo() durations are also ms). See conventions.

easing receives the raw linear progress 0..1 and returns the eased progress — see easings. onComplete fires when progress reaches 1; a tween stopped early never fires it.

Value kinds

The shape of to selects the interpolation:

to Interpolation onUpdate receives
number lerp number
array length 2 (Vec2-like) per-component lerp Float32Array (reused)
array length 3 (Vec3-like) per-component lerp Float32Array (reused)
array length 4 (Quat-like) quaternion slerp Float32Array (reused)
Note

for the array kinds, onUpdate receives the same backing Float32Array every frame — it's overwritten in place. Don't store the reference; copy ([...v]) if you need to keep a frame's value.

Note

length-4 arrays are always treated as quaternions (slerped, with hemisphere flip) — don't tween an RGBA tuple as a length-4 array.

Not implemented

string values (colors). The color branch in the source is dead code (it compares the value, not the type), so a string to falls through to the array-length checks and produces nothing useful — NaN output for 2/3/4-character strings, a complete no-op otherwise. No error is thrown. Animate a color by tweening a number 0..1 and mixing the two colors yourself in onUpdate.

Controlling a running tween

TypeScript
stopAnimation(id: number): void     // halt permanently — no further onUpdate, no onComplete
pauseAnimation(id: number): void    // freeze progress
resumeAnimation(id: number): void   // continue a paused tween

animateMat4 — tweening a transform matrix

TypeScript
animateMat4({
  from: Mat4Like,           // Float32Array | number[], 16 elements
  to: Mat4Like,
  duration: number,         // milliseconds
  onUpdate(m: Float32Array): void,
  onComplete?(): void,
  easing?: Easing,
}): number

Element-wise matrix lerp would shear; animateMat4 instead decomposes both matrices once at call time into position / rotation / scale, lerps position and scale, slerps rotation, and recomposes each frame. onUpdate receives a reused 16-element Float32Array — same don't-store-the-reference rule as the vector kinds.

Pitfalls

TypeScript
// ✗ duration in seconds — the tween finishes in under a frame
animate({ from: 0, to: 100, duration: 0.5, onUpdate: v => { node.x = v } })
// ✓ milliseconds
animate({ from: 0, to: 100, duration: 500, onUpdate: v => { node.x = v } })

// ✗ storing the vec value — every entry ends up the same (final) array
animate({ from: [0, 0], to: [10, 10], duration: 300, onUpdate: v => path.push(v) })
// ✓ copy the frame's value
animate({ from: [0, 0], to: [10, 10], duration: 300, onUpdate: v => path.push([...v]) })

See also

  • EasingseaseIn / easeOut / easeInOut, cubicBezier, custom easings
  • Conventions — time — where the SDK uses seconds vs milliseconds
  • Host globalssetLoop for open-ended per-frame animation