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:
- Timestamp Tracking: Each frame triggered by
requestAnimationFramepasses a high-resolution timestamp (DOMHighResTimeStamp). The runner compares this value against the timestamp of the previous frame to calculate the actual elapsed time (delta). - Delta Smoothing: To avoid sudden spikes caused by
brief browser stutters or background tasks,
Matter.Runnermaintains a rolling average of recent frame times. - 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:
- Fixed Time Stepping (Default): By default,
runner.isFixedis set tofalse, but the runner targets a fixed time step (typically 16.666 ms, corresponding to 60 FPS). Even on higher refresh rate monitors, the runner calculates the ratio of elapsed time relative to the target frame rate and steps the physics accordingly. - Variable Time Stepping: If configured,
Matter.Runnercan pass the raw, smoothed delta directly intoEngine.update(engine, delta). This advances the simulation proportionally to the actual duration since the last render, ensuring that movements appear fluid regardless of display hardware.
The Runner Execution Sequence
During each refresh cycle, Matter.Runner executes
operations in a deterministic sequence:
- Before Tick: Emits the
beforeTickevent, allowing custom logic to run before any timing or engine updates occur. - Tick Evaluation: Evaluates frame timing, verifies
whether enough time has passed to warrant an engine step, and emits the
tickevent. - Engine Update: Invokes
Matter.Engine.update(engine, delta)to resolve collisions, compute constraints, and update body positions. - After Tick: Emits the
afterTickevent, signaling that physics states have settled and external renderers (such asMatter.Renderor custom WebGL/Canvas pipelines) can draw the updated world. - Next Frame Registration: Requests the next frame
via
requestAnimationFrame, maintaining continuous synchronization with the monitor.