Skip to content

The game loop

samme edited this page Nov 4, 2024 · 14 revisions

Phaser's game loop uses a variable step with delta time scaling. Some systems (Arcade Physics, Tweens) use fixed steps within the main step. Matter Physics uses its own variable-step loop, which can be problematic.

The game loop's now and delta are the values passed as time and delta to the scene update() and similar callbacks. You should use delta for any of your own timing logic (i.e., apart from Phaser's animations, physics, timers, and tweens). Never use actualFps for timing logic; it's intended as a summary statistic.

Timing

The game loop steps based on requestAnimationFrame() (preferred) or setTimeout() at a target interval. Unless you use limit, the game makes one step (update and render) per animation frame.

requestAnimationFrame() generally matches the display refresh rate (commonly 60Hz, higher on fast displays), but it can be lower (e.g., 30Hz) when a device is in low-power mode or the game is running in a different-origin document frame that the user hasn't clicked on. requestAnimationFrame() is usually paused by the browser in background tabs or hidden document frames, so the game won't step at all then.

setTimeout() generally follows the target interval or a little slower, but it's not necessarily synchronized to the display.

You can check the frame rate distribution for requestAnimationFrame() on your device.

Let's look at the game loop timing in the context of its default configuration.

name default value description
min 5 The maximum acceptable delta is 200 ms (5 fps).
target 60 The expected delta is 16.6 ms (60 fps).
limit 0 No rate limit. Every animation frame from the browser causes one game step with the frame delta.
forceSetTimeOut false Use requestAnimationFrame().
deltaHistory 10 Use 10 consecutive frame deltas to calculate the smoothed value.
panicMax 120 During cool down, use the target delta (16.6 ms) for 120 frames instead of the actual delta.
smoothStep true Smooth the raw deltas and discard any deltas above 200 ms.

View these in a running Phaser game.

Some configurations

When forceSetTimeOut is off, you probably shouldn't change target.

You could increase min, but it should be much smaller than target, otherwise you'll produce time distortion from too much clamping.

The default panicMax is rather large, so you could reduce it or set it to 0 to disable it.

Turning off smoothStep turns off delta smoothing, clamping, and cool down.

Limiting FPS with limit

new Phaser.Game({
  fps: {
    limit: 33,
    target: 30
  }
});
new Phaser.Game({
  fps: {
    limit: 66,
    target: 60
  }
});

When you set limit, the game loop skips animation frames from the browser until the minimum delta is reached. (actualFps will show the browser's frame rate, not the game's step rate.)

On a 60Hz display, a 33 fps limit is smoother than a 30 fps limit because of the variance in animation frame deltas.

Limiting FPS with forceSetTimeOut and target

new Phaser.Game({
  fps: {
    forceSetTimeOut: true,
    target: 30
  }
});

Increasing the update rate with forceSetTimeOut and target

For testing your game's logic with a small delta, you can use a large target. Just bear in mind that any extra renders are wasted!

new Phaser.Game({
  fps: {
    // For testing only, try deltas as small as 4-5 ms.
    // 240 Hz is a hard maximum for `setTimeout()`.
    forceSetTimeOut: true,
    target: 240
  }
});

Cool down ☃️

The cool down feature happens when the game starts and if the user returns to a browser tab or document frame after leaving (refocusing). During the cool down period, the game loop ignores the actual deltas from the browser and uses the target delta instead until panicMax frames have elapsed. The purpose is to provide a consistent delta value during a period when the actual deltas can be unstable, but it can cause time distortion (for the period of the cool down) if the actual deltas are very different from the target (16.6 ms by default).

Clone this wiki locally