Matter.js Runner Tick Event Explained

In Matter.js, the Matter.Runner coordinates the physics simulation loop with the browser's render cycles using requestAnimationFrame. This article explains what the tick event is on Matter.Runner, where it fits within the execution lifecycle, how it differs from Matter.Engine update events, and how to implement it to manage frame-by-frame simulation logic.

What Is Matter.Runner?

Matter.Runner is an optional utility provided by Matter.js that runs an animation loop. Instead of manually invoking Engine.update(engine, delta) inside a native requestAnimationFrame callback, the runner automatically handles variable frame rates, fixed time steps, and execution synchronization.

The tick Event

The tick event is triggered on the runner instance on every iteration of the runner's game loop. It indicates that a frame update cycle is currently taking place.

During an execution loop, Matter.Runner fires three primary lifecycle events in this order:

  1. beforeTick: Fires immediately at the start of a frame before any timing calculations or physics updates occur.
  2. tick: Fires during the frame processing step, right as the runner coordinates the timing and updates the Matter.Engine.
  3. afterTick: Fires at the end of the frame once all physics updates and event handling for that frame are complete.

Event Object Properties

When listening to the tick event, the event callback receives an event object containing execution details:

Runner tick vs. Engine beforeUpdate / afterUpdate

It is common to confuse runner events with engine events.

If the runner is configured to use a fixed time step or needs to catch up due to frame drops, it may trigger multiple engine updates inside a single runner tick, or it may skip an engine update entirely. Using the runner's tick event is ideal when synchronizing systems to the display refresh rate, whereas engine events are better suited for logic that must strictly map to physics state changes.

How to Use the tick Event

To listen to the tick event, use the Matter.Events.on method:

const { Engine, Runner, Events } = Matter;

// Create engine and runner
const engine = Engine.create();
const runner = Runner.create();

// Attach listener to the runner's tick event
Events.on(runner, 'tick', (event) => {
    // Current frame timestamp
    const currentTime = event.timestamp;

    // Custom frame-based logic (e.g., input polling, custom rendering, stats tracking)
    console.log(`Runner tick executed at: ${currentTime}`);
});

// Start the simulation loop
Runner.run(runner, engine);

Common Use Cases