How Matter.Runner Syncs with Monitor Refresh Rate

This article explains how Matter.Runner synchronizes physics simulation steps with the user's monitor refresh rate in Matter.js. By utilizing the browser's native frame scheduling, calculating frame time deltas, and regulating execution intervals, the runner ensures that physics calculations remain smooth, consistent, and bound to the display's vertical sync (V-Sync).

Browser Scheduling via requestAnimationFrame

At its core, Matter.Runner relies on the browser's window.requestAnimationFrame() API rather than standard JavaScript timers like setInterval or setTimeout. When you start the runner using Matter.Runner.run(runner, engine), it registers a recursive loop with requestAnimationFrame.

Because requestAnimationFrame pauses execution when the browser tab is inactive and executes callbacks immediately before the display repaints, the physics engine's update cycle naturally aligns with the monitor's refresh rate (such as 60Hz, 120Hz, or 144Hz).

Frame Timing and Delta Calculation

To prevent physics simulations from running too fast or too slow on displays with different refresh rates, Matter.Runner monitors the elapsed time between frames:

  1. Timestamp Tracking: Each frame triggered by requestAnimationFrame passes a high-resolution timestamp (DOMHighResTimeStamp). The runner compares this value against the timestamp of the previous frame to calculate the actual elapsed time (delta).
  2. Delta Smoothing: To avoid sudden spikes caused by brief browser stutters or background tasks, Matter.Runner maintains a rolling average of recent frame times.
  3. Delta Clamping: If a frame takes too long—such as when a tab is minimized and reopened—the runner caps the maximum allowable delta. This prevents physics bodies from tunneling through boundaries or exploding due to excessive time accumulation.

Fixed vs. Variable Time Steps

Matter.Runner supports two modes of stepping the simulation inside Matter.Engine.update:

The Runner Execution Sequence

During each refresh cycle, Matter.Runner executes operations in a deterministic sequence:

  1. Before Tick: Emits the beforeTick event, allowing custom logic to run before any timing or engine updates occur.
  2. Tick Evaluation: Evaluates frame timing, verifies whether enough time has passed to warrant an engine step, and emits the tick event.
  3. Engine Update: Invokes Matter.Engine.update(engine, delta) to resolve collisions, compute constraints, and update body positions.
  4. After Tick: Emits the afterTick event, signaling that physics states have settled and external renderers (such as Matter.Render or custom WebGL/Canvas pipelines) can draw the updated world.
  5. Next Frame Registration: Requests the next frame via requestAnimationFrame, maintaining continuous synchronization with the monitor.