Decouple Matter.js Physics for Headless Benchmarks

By default, Matter.js couples simulation updates to the browser display refresh rate using Matter.Runner and requestAnimationFrame. Decoupling physics updates from screen rendering allows you to run headless simulations at maximum processor speed, making it possible to execute automated stress tests, profile computational bottlenecks, and measure engine throughput. This guide demonstrates how to bypass Matter.js rendering utilities, implement a manual execution loop, and collect accurate benchmark metrics.

Why Avoid Matter.Runner and Matter.Render

Matter.Runner synchronizes simulation steps with the screen's refresh cycle (typically 60Hz or 144Hz). It calculates a dynamic delta based on wall-clock time and caps the execution rate to prevent physics updates from outpacing frame delivery. Similarly, Matter.Render consumes significant CPU and GPU overhead drawing canvas elements.

To benchmark raw physics performance, both must be eliminated:

Implementing a Synchronous Headless Loop

To execute physics calculations as fast as possible, call Engine.update() inside a standard iteration loop. Passing a fixed delta (such as 1000 / 60 for 16.666ms) ensures deterministic physics across benchmark runs regardless of hardware execution speed.

import Matter from 'matter-js';

const { Engine, World, Bodies } = Matter;

// 1. Initialize the Engine without Runner or Render
const engine = Engine.create();

// 2. Populate the World
const ground = Bodies.rectangle(400, 610, 810, 60, { isStatic: true });
const stack = [];
for (let i = 0; i < 500; i++) {
  stack.push(Bodies.circle(400, 100, 10));
}
World.add(engine.world, [ground, ...stack]);

// 3. Define Benchmark Parameters
const TOTAL_FRAMES = 1000;
const FIXED_DELTA = 1000 / 60; // 16.66ms per step

// 4. Run the Headless Execution Loop
const startTime = performance.now();

for (let frame = 0; frame < TOTAL_FRAMES; frame++) {
  Matter.Engine.update(engine, FIXED_DELTA);
}

const endTime = performance.now();
const durationMs = endTime - startTime;

// 5. Calculate Metrics
const framesPerSecond = (TOTAL_FRAMES / (durationMs / 1000)).toFixed(2);
const msPerFrame = (durationMs / TOTAL_FRAMES).toFixed(3);

console.log(`Simulated ${TOTAL_FRAMES} frames in ${durationMs.toFixed(2)}ms`);
console.log(`Throughput: ${framesPerSecond} steps/sec (${msPerFrame} ms/step)`);

Best Practices for Accurate Benchmarking

When running headless Matter.js tests, apply these practices to isolate physics computation: