Skip to content

Timeline

The Timeline system drives multi-track animations that coordinate property changes, Spine playback, sprite animations, audio events, and entity activation over time. Timelines are created in the editor’s Timeline panel and played back at runtime via the TimelinePlayer component and TimelineControl API.

Tutorial: Animate a Sprite

Let’s create a timeline that moves a sprite from left to right and fades it out.

Step 1: Select the Entity

Click on the entity you want to animate in the Hierarchy.

Step 2: Open the Timeline Panel

Open ViewTimeline (or find it in the panel menu). If the entity doesn’t have a TimelinePlayer component yet, the panel shows a Create button — click it to add one automatically.

Step 3: Add a Property Track

  1. Click the + button in the track list
  2. Select PropertyTransformposition.x
  3. A new track appears, targeting the selected entity’s Transform.position.x

Step 4: Add Keyframes

  1. Move the playhead to time 0.0 by clicking the ruler at the top
  2. Click the diamond icon (or press K) to add a keyframe — it captures the current value
  3. Move the playhead to 1.0 seconds
  4. In the Inspector, change position.x to 200
  5. Press K again — a second keyframe is added with the new value

Step 5: Add a Second Track

  1. Click +PropertySpritecolor.a
  2. At time 0.0, add a keyframe (value: 1.0)
  3. At time 1.0, add a keyframe (value: 0.0)

Step 6: Preview

Press Space to play in the editor. The sprite moves right while fading out over 1 second.

Step 7: Configure Playback

In the Inspector, set the TimelinePlayer properties:

  • wrapMode: loop to make it repeat
  • speed: 0.5 to play at half speed

Track Targeting

Every track has a target entity. By default, it’s the entity that owns the TimelinePlayer component.

Animating Child Entities

To animate a child entity, set the track’s Child Path — a /-separated name hierarchy:

Arm/Hand → find child "Arm", then its child "Hand"
Body → find direct child "Body"
(empty) → target the root entity itself

In the editor, when you add a track while a child entity is selected, the child path is set automatically.

Track Types

Property

Animate any numeric component property with keyframe interpolation.

Built-in properties (optimized, computed in C++):

ComponentProperties
Transformposition.x/y/z, scale.x/y/z, rotation
Spritecolor.r/g/b/a, opacity, size.x/y
UIRectoffsetMin.x/y, offsetMax.x/y, anchorMin.x/y, anchorMax.x/y, pivot.x/y
CameraorthoSize

Custom properties: Any other numeric component field can be animated using nested paths (e.g., myComponent.health).

Keyframe Interpolation

Right-click a keyframe to choose an interpolation mode:

ModeBehavior
HermiteSmooth cubic curve using tangent handles (default)
LinearStraight line between keyframes
StepHold value until next keyframe (no interpolation)
EaseInSlow start, accelerating toward the next keyframe
EaseOutFast start, decelerating toward the next keyframe
EaseInOutSmooth acceleration and deceleration

For Hermite mode, drag the tangent handles in the curve editor to shape the interpolation.

Spine

Trigger Spine animation clips on a SpineAnimation component. Each keyframe specifies an animation name to play at that time.

Sprite Animation

Play sprite animation clips. Each keyframe specifies a clip name to start at that time.

AnimFrames

A visual frame track for sprite-sheet-style animation. Unlike sprite animation clips, each frame is displayed as a colored, resizable block on the timeline:

  • Each block references a texture and has its own duration (default: 1/12 second)
  • Drag block edges to adjust frame timing
  • The target entity must have a Sprite component — the texture is swapped each frame

Use AnimFrames when you need per-frame timing control without creating a separate animation clip.

Audio

Trigger audio playback at specific times. Each keyframe specifies an audio asset to play.

Activation

Toggle entity visibility over time ranges. The entity is enabled within the active range and disabled outside it.

Marker

Place named markers at specific times. Markers don’t affect playback — they serve as reference points for the editor and for seeking.

Custom Event

Trigger named events at specific times. Listen for these events in your systems to run custom logic at precise moments during the animation.

Editor Controls

Keyboard Shortcuts

ShortcutAction
SpacePlay / Pause
, / .Step backward / forward one frame
KAdd keyframe at playhead position
DeleteDelete selected keyframes
Ctrl+ClickToggle keyframe selection
Shift+ClickRange select keyframes
Box dragRubber-band select multiple keyframes

Toolbar

  • Record: Toggle auto-keyframe mode — property changes in the Inspector are automatically recorded as keyframes at the current playhead position
  • Speed: Cycle playback speed (0.25x, 0.5x, 1x, 2x, 4x)
  • Wrap Mode: Cycle between Once, Loop, and PingPong
  • Snap: Toggle grid snapping during keyframe drag
  • Duration: Double-click the time display to edit total timeline duration

Multi-Select & Batch Operations

Select multiple keyframes (Ctrl+Click, Shift+Click, or box drag), then:

  • Drag to move them all together
  • Delete to remove them as a single undo step

Track Management

  • Drag tracks to reorder in the list
  • Right-click a track for rename / delete
  • Each track type has a distinct color indicator in the sidebar

Live Playhead Sync

When entering Play Mode, the timeline automatically switches to LIVE mode. The playhead tracks the runtime animation position in real-time (polled at 60fps), and a red blinking “LIVE” indicator appears in the toolbar. Editor playback controls are disabled during live mode and restore when exiting Play Mode.

TimelinePlayer Component

Add TimelinePlayer to an entity to play a timeline asset at runtime.

PropertyTypeDefaultDescription
timelinestring''Path to the .estl timeline asset
playingbooleanfalseSet to true to start playback
speednumber1.0Playback speed multiplier
wrapModestring'once''once', 'loop', or 'pingPong'

When playing is set to true, the timeline starts. When it finishes (in once mode), playing is automatically set back to false.

WrapMode

ValueDescription
oncePlay once and stop at the end
loopRestart from the beginning when finished
pingPongAlternate between forward and reverse playback

TimelineControl API

Use TimelineControl for runtime playback control from systems:

import { defineSystem, addSystem, Query, TimelinePlayer, TimelineControl } from 'esengine';
import { Res, Input } from 'esengine';
addSystem(defineSystem(
[Res(Input), Query(TimelinePlayer)],
(input, query) => {
for (const [entity] of query) {
if (input.isKeyPressed('Space')) {
TimelineControl.play(entity);
}
if (input.isKeyPressed('KeyP')) {
TimelineControl.pause(entity);
}
}
}
));

Methods

MethodReturnsDescription
TimelineControl.play(entity)voidStart or resume playback
TimelineControl.pause(entity)voidPause playback
TimelineControl.stop(entity)voidStop and reset to the beginning
TimelineControl.setTime(entity, time)voidSeek to a specific time (seconds)
TimelineControl.isPlaying(entity)booleanCheck if timeline is playing
TimelineControl.getCurrentTime(entity)numberGet current playback time (seconds)

Registering Timeline Assets

When loading timelines outside the editor (e.g., in a custom asset pipeline), use the registration API:

import { registerTimelineAsset, getTimelineHandle, parseTimelineAsset } from 'esengine';
const asset = parseTimelineAsset(jsonData);
registerTimelineAsset('timelines/intro.estl', asset);
const tl = getTimelineHandle('timelines/intro.estl');

Example: Intro Sequence

import {
defineSystem, addStartupSystem, addSystem,
Query, Mut, TimelinePlayer, TimelineControl, Res, Input
} from 'esengine';
// Play the intro timeline on startup
addStartupSystem(defineSystem(
[Query(Mut(TimelinePlayer))],
(query) => {
for (const [entity, player] of query) {
if (player.timeline === 'timelines/intro.estl') {
TimelineControl.play(entity);
}
}
}
));
// Allow skipping with Space
addSystem(defineSystem(
[Res(Input), Query(TimelinePlayer)],
(input, query) => {
if (input.isKeyPressed('Space')) {
for (const [entity] of query) {
if (TimelineControl.isPlaying(entity)) {
TimelineControl.stop(entity);
}
}
}
}
));

Resource Cleanup

Timeline and tween resources attached to an entity are automatically cleaned up when the entity is despawned. The engine calls _tl_destroy to free C++ timeline objects and Tween.cancelAll() to cancel active tweens via onDespawn. You do not need to manually stop timelines or cancel tweens when destroying entities.

Next Steps