Time

The game clock: how fast the game runs and what a frame is worth. Time.scale slows or speeds up the whole game, Time.paused freezes it, and both reach physics, animators and particles. Wall-clock delays in an app are setTimeout / setInterval (globals); dates are date (datetime).

  • pause the game and freeze physics and animations
  • keep a pause menu animating while the game is paused
  • slow motion or speed up the whole game
  • move something by the frame's delta time
  • run logic at a fixed 60 Hz step
  • read the game time or the frame count since the start

Platforms: every host. In a 2D-only app (no 3D Scene) on Android, in the windowless server (lecodes serve) and in headless tests, the clock starts only once the 2D engine has begun loading a texture — until then Time.frame stays 0 and no update runs.

At a glance

TypeScript
class PauseMenu extends System<'pauseMenu'> {
  updateWhilePaused = true                    // keeps ticking while the world is frozen
  fade = 0
  private onKey = (e: InputKeyEvent) => { if (e.code === 'Escape' && !e.repeat) Time.paused = !Time.paused }
  onAttach() { Input.on('keydown', this.onKey) }
  onDetach() { Input.off('keydown', this.onKey) }
  update() {                                  // dt is 0 while paused: use the wall clock
    this.fade = Time.paused ? Math.min(1, this.fade + Time.unscaledDt * 4) : 0
  }
}

const scene = new Scene().system(PauseMenu)
scene.open()
Time.scale = 0.25                             // slow motion for a kill cam

Game clock properties: Time.dt, now, scale, paused

TypeScript
Time.scale: number         // 1 normal · 0.5 half speed · 2 double (≤ 0 / NaN / Infinity → 0, frozen)
Time.paused: boolean       // freeze the game; `scale` is remembered and restored on resume
Time.effectiveScale        // the multiplier the engines run at right now (0 while paused)
Time.dt: number            // this frame's delta in GAME seconds — what update(dt) receives; 0 while paused
Time.unscaledDt: number    // this frame's wall-clock delta, seconds (the engines clamp it to 0.05)
Time.fixedDt: number       // 1/60 — what updateFixed(dt) always receives (read-only)
Time.now: number           // game seconds since the clock started (scaled; stops while paused)
Time.unscaledNow: number   // wall-clock seconds since the clock started
Time.frame: number         // frames dispatched since the clock started (paused frames count)

Frame delta time: update(dt)

update(dt) / updateBefore(dt) receive Time.dt: game seconds, scaled by Time.scale, 0 while paused. Multiply speeds by it and the motion is the same at any frame rate.

TypeScript
class Spinner extends Aspect<'spinner', Sprite> {
  speed = 90                                  // degrees per GAME second
  update(dt: number) {                        // dt = Time.dt: slows with Time.scale, 0 while paused
    this.node.rotation += this.speed * dt
  }
}

const scene = new Scene2D()
scene.add(new Sprite({ size: [40, 40], color: '#4f7cff' }).aspect(Spinner))
scene.open()

The clock is driven by the aspect dispatch, which starts with the first aspect or system that defines an update method: before that, dt, now and frame stay 0.

Pause the game, keep the pause menu animating: Time.paused, updateWhilePaused

Time.paused = true pauses the game: physics, animators and particles stop, and every updater is skipped except those with updateWhilePaused = true — a pause menu's fade keeps animating there. Those are called with dt = 0 and read Time.unscaledDt themselves — the same split as Unity's deltaTime / unscaledDeltaTime (the pause menu of At a glance).

Fixed step: updateFixed and Time.fixedDt

updateFixed(dt) always receives Time.fixedDt (1/60); scale changes how many fixed steps a frame holds, never their length. While paused there are none — not even for updateWhilePaused.

TypeScript
class Drift extends Aspect<'drift', Sprite> {
  speed = 120                                 // pixels per game second
  updateFixed(dt: number) {                   // dt is always Time.fixedDt (1/60)
    this.node.x += this.speed * dt
  }
}

const scene = new Scene2D()
scene.add(new Sprite({ size: [32, 32], color: '#ff7a45' }).aspect(Drift))
scene.open()

Slow motion for physics, animators and particles: Time.scale

The scale is pushed to each engine through its setTimeScale bridge (feature-detected): physics steps, animator clocks and particle systems advance by dt × scale. A host whose engine predates the hook keeps its simulations at wall-clock speed while game code still scales — pause the bodies yourself there.

Tweens on the 'game' clock (animate) take the 3D engine's scale: they slow down and pause with Time only once a 3D Scene exists. In a 2D- or UI-only app, on the web and in headless tests, a 'game' tween runs at wall-clock speed.

Timers are not on the game clock: setTimeout, setLoop

setTimeout / setInterval / setLoop are host timers on the wall clock — they neither scale nor pause with Time (globals). A delay that must stop while the game is paused counts dt in an aspect or system.

See also

  • Aspects — updateWhilePaused, the phases that consume dt
  • Input — Input.on('keydown') for one-shot keys like the pause toggle
  • Host globals — setLoop, setTimeout