Skip to content

tween

Import from @pierre/ecs/modules/tween.

The Tween value primitive — interpolates a number from → to over a duration along an easing curve.

It composes existing primitives rather than reinventing them: a Timer tracks time, an Easing curve reshapes progress, and lerp from modules/math does the value interpolation.

A pure value (like Timer / Spawner), not an ECS component — read the current value each tick wherever you animate (render code, a system, a callback). Single-channel; for multi-channel (x + y, RGB) run one tween per channel.

interface Tween {
from: number;
to: number;
readonly easing: Easing;
readonly timer: Timer;
}
makeTween(durationMs, from, to, easing?, mode?): Tween // default easing linear, mode 'once'
tickTween(tw, dtMs): number // advance + return current eased value
tweenValue(tw): number // current eased value without advancing
tweenDone(tw): boolean // finished ('once') / per-wrap edge ('repeating')
resetTween(tw, { from?, to?, durationMs? }?): void // restart, optionally retarget

tickTween is tickTimer(timer, dt) then lerp(from, to, easing(fraction(timer))).

import { makeTween, tickTween, tweenDone } from '@pierre/ecs/modules/tween';
import { easeOutBack } from '@pierre/ecs/modules/easing';
// Pop a pickup in with a little overshoot:
const pop = makeTween(250, 0, 1, easeOutBack);
// each frame:
const scale = tickTween(pop, dtMs);
if (tweenDone(pop)) { /* settle */ }
// Re-aim a value mid-flight (e.g. chase a new target):
resetTween(pop, { from: scale, to: 1.2, durationMs: 120 });
  • TweenDef ECS component + system — deferred. No current consumer animates via a component (they read tweenValue inline). Add when a consumer needs declarative per-entity tweens.
  • Multi-channel / vector tweens — run one Tween per channel for now.

Import via @pierre/ecs/modules/tween. Depends on modules/timer, modules/easing, and modules/math.