How to Update Matter.js Engine Manually

This article explains how to manually update the physics simulation in Matter.js instead of relying on the built-in runner. By using the Matter.Engine.update method within your own game loop, you can achieve precise control over frame rates, pause states, physics synchronization with rendering libraries like PixiJS or Three.js, and variable time steps like slow-motion effects.

Bypassing the Built-In Runner

By default, Matter.js provides Matter.Runner to automatically step the physics world forward using the browser's refresh rate. To manually control updates, you simply omit initializing the runner or stop an existing one:

// Stop an existing runner if one is active
Matter.Runner.stop(runner);

Once the runner is stopped or avoided entirely, the engine will only advance when you explicitly command it to.

Using Engine.update

The core function for manual advancement is Matter.Engine.update(engine, [delta]).

// Advance the simulation by one standard frame (16.67ms)
Matter.Engine.update(engine);

// Or advance by a custom delta (e.g., 20ms)
Matter.Engine.update(engine, 20);

Implementing a Custom Loop with requestAnimationFrame

To integrate Matter.js into a standard render loop, call Matter.Engine.update() inside a requestAnimationFrame callback.

const { Engine, World, Bodies } = Matter;

const engine = Engine.create();

let lastTime = performance.now();

function gameLoop(currentTime) {
    // Calculate the elapsed time in milliseconds
    const delta = currentTime - lastTime;
    lastTime = currentTime;

    // Manually step the physics engine
    Matter.Engine.update(engine, delta);

    // Run custom rendering or game logic here

    requestAnimationFrame(gameLoop);
}

// Start the loop
requestAnimationFrame(gameLoop);

Implementing a Fixed Time Step

Physics engines can become unstable if the delta time fluctuates too wildly. For the most consistent collision detection and deterministic behavior, use a fixed time step combined with an accumulator:

const fixedDelta = 1000 / 60; // 60 updates per second
let accumulator = 0;
let lastTime = performance.now();

function fixedLoop(currentTime) {
    const frameTime = currentTime - lastTime;
    lastTime = currentTime;

    // Prevent spiral of death on long frame delays
    accumulator += Math.min(frameTime, 250);

    // Consume accumulated time in fixed chunks
    while (accumulator >= fixedDelta) {
        Matter.Engine.update(engine, fixedDelta);
        accumulator -= fixedDelta;
    }

    // Render your graphics here

    requestAnimationFrame(fixedLoop);
}

requestAnimationFrame(fixedLoop);

Controlling Simulation Speed

Because you control the delta argument passed to Engine.update(), you can manipulate the perceived passage of time directly: