What is a Timeline?
Learn the core concepts—actions, updates, until, orchestration, and parallelism.
Timelines let you write 3D behaviors like a story using async generator functions. You yield actions, wait for conditions, and compose sequences, loops, and parallel flows in a readable way.
At a high level, timelines are composed of actions and sub‑timelines. You chain them sequentially or run them in parallel to build rich behaviors.
Actions
An action is a unit of work you yield* from a timeline. It can:
- provide an
update(state, clock)called every frame; returnfalseto finish - provide an
untilpromise to decide when it finishes - or both to run something every frame until another event happens
Use an action when you want to perform work over time, wait for something to happen, or both.
What does an action look like?
yield* action({
update: (state, clock) => {
// apply changes every frame
return false // finish immediately
},
})
Waiting with until
yield* action({ until: timePassed(1, 'seconds') })
How long does an action run?
An action runs until either:
- its
updatereturnsfalse, or - its
untilpromise resolves.
If both are provided, the action keeps updating until the until resolves.
The action clock
The clock passed to update(state, clock) contains:
delta: seconds since last frame (internally clamped to ~1/30s)prevDelta: previousdeltaorundefinedon the first frame (used byspring/velocityto infer velocity)timelineTime: total time since the timeline startedactionTime: time since the current action started (resets between actions)
Timelines
A timeline is an async generator that yields actions (and can yield other timelines). You can loop, wait, and branch—writing behavior like a story.
Reusable timelines
A reusable timeline is a function that returns a timeline so it can be started many times.
async function* reusableFadeIn() {
yield* action({ until: timePassed(0.5, 'seconds') })
}
// later inside another timeline
yield* reusableFadeIn()
Running timelines
- React: use
useTimeline(reusableTimeline, deps); it wires intouseFrameand restarts ondepschange. - Vanilla: use
start(reusableTimeline)to get an update function you call each frame withdeltaSeconds.
// React
useTimeline(() => reusableFadeIn(), [someDep])
// Vanilla
const update = start(reusableFadeIn)
// in your render loop
update(state, deltaSeconds)
Orchestration
You can chain actions/timelines sequentially or run them concurrently.
Sequential chaining
- Sequential:
yield* action(A); yield* action(B);runs A, then B.
// Sequential
yield* action(A)
yield* action(B)
Parallel composition
- All:
yield* parallel('all', A, B)continues when both finish - Race:
yield* parallel('race', A, B)continues when the first finishes
yield* parallel('all', A, B)
yield* parallel('race', A, B)
Sub-timelines
Timelines compose: you can yield* another reusable timeline anywhere. This keeps complex behaviors modular and readable.
// Reusable timeline
async function* someReusableTimeline() {
yield* action({ until: timePassed(0.5, 'seconds') })
}
// Compose inside another timeline
yield* someReusableTimeline()
Graph timelines
Model behaviors as named states and transitions. graph(initial, stateMap) drives the flow; useTimelineGraph is the React wrapper.
yield* graph('Idle', {
Idle: {
timeline: () => action({ update: ... }),
transitionTo: { Run: { whenPromise: () => timePassed(1, "seconds") } }
},
Run: {/* ... */},
})
Built‑ins for motion
- Action updates:
transition,lookAt,offsetDistance,offsetRotation - Eases:
spring(preset|config),time(durationSec),velocity(speed, maxAccel?) - Helpers:
worldSpace(type, object),property(object, key), waits liketimePassed,mediaFinished,animationFinished, plusdoUntil/doWhile
Explore full, runnable examples in the tutorials:
- Building your First Timeline
- Building Complex Timelines using Parallel
- Building Graph Timelines
- Use pmndrs/timeline in vanilla three.js