Skip to content

Timelines

Timelines sequence multiple tween segments, callbacks, and labels over time. Under the hood, each segment spawns Tween entities lazily as the playhead reaches them, which are then processed by the batch TweenSystem.

import { AnimationTimeSource } from '@shell/animate';
const animate = resources.get('animate');
const tl = animate.timeline({
loop: -1, // -1 = infinite, 0 = no loop
alternate: true, // ping-pong direction each loop
autoplay: true, // start immediately (default)
playbackRate: 1, // speed multiplier
timeSource: AnimationTimeSource.Simulation,
});
PropertyTypeDefaultDescription
loopnumber0Number of loops (-1 = infinite)
alternatebooleanfalseAlternate direction each loop
autoplaybooleantrueStart playback immediately
playbackRatenumber1Speed multiplier (2 = 2x speed)
timeSourceAnimationTimeSourceAnimationTimeSource.SimulationClock used by the playhead and every child tween

A timeline owns one time source for its playhead, callbacks, loops, offsets, and child tweens. Segment options cannot select a different clock. Wall-time timelines use the ticker’s unscaled frame delta and stop advancing when the ticker is paused or stopped.

tl.add(entity, Position, { x: 100, duration: 500 }, 0) // absolute time 0
.add(entity, Position, { y: 200, duration: 500 }, '+=200') // 200ms after previous end
.set(entity, Opacity, { value: 0 }, '<') // same start as previous
.call(() => console.log('halfway'), 350) // callback at 350ms
.label('midpoint', 350) // named position
.add(entity, Position, { x: 0, duration: 500 }, 'midpoint'); // at label
PositionMeaning
numberAbsolute time in milliseconds
'+=N'N ms after the previous segment ends
'-=N'N ms before the previous segment ends
'<'Same start time as the previous segment
'<+=N'N ms after the previous segment starts
'<-=N'N ms before the previous segment starts
'labelName'At the named label position
undefinedSequential — at the end of the timeline
MethodDescription
add(entity, component, properties, position?)Add a tween segment
set(entity, component, properties, position?)Add a zero-duration tween (immediate property set)
call(callback, position?)Schedule a callback at a position
label(name, position?)Define a named position for later reference
play()Start or resume playback
pause()Pause playback and all active tween handles
reverse()Flip playback direction
restart()Reset to the beginning and start playing
seek(time)Jump to a specific time in milliseconds
stretch(newDuration)Scale all segments to a new total duration
cancel()Stop, despawn all tweens, and remove from active list
complete()Jump to the end and fire completion
then(callback?)Returns a Promise<void> that resolves on completion
PropertyTypeDescription
currentTimenumberCurrent playback time in ms
totalDurationnumberTotal timeline duration in ms
isPlayingbooleanWhether the timeline is playing

When loop is set and the timeline reaches the end, it loops. With alternate: true, playback reverses direction each loop (ping-pong). Segments and callbacks are reset at the start of each loop.

Callbacks fire once when the playhead passes their position:

tl.call(() => console.log('started'), 0);
tl.call(() => console.log('finished'), 'end');
tl.label('end', 2000);

Labels can be created at any position and referenced by name in subsequent add(), set(), or call() calls.

Scale the entire timeline proportionally:

tl.stretch(5000); // Expand or shrink to 5 seconds total

All segment offsets, durations, and label positions are multiplied by the ratio.

Await timeline completion:

await tl.then();
// or
await tl.then(() => console.log('done'));

Timelines are advanced automatically by the TimelineSystem registered at PostUpdate. You do not need to call __update manually.