liabru/matter-js · error · Error

Matter.Runner: missing required global window.requestAnimati

Error message

Matter.Runner: missing required global window.requestAnimationFrame.

What it means

Runner._onNextFrame schedules the next animation frame via window.requestAnimationFrame, which is required for the runner loop to advance. In environments where window or requestAnimationFrame does not exist (e.g. Node.js, Web Workers, headless/jsdom setups), the library deliberately throws instead of failing silently. This is an environment-capability guard, not a logic bug.

Source

Thrown at src/core/Runner.js:234

     * @param {runner} runner
     */
    Runner.stop = function(runner) {
        Runner._cancelNextFrame(runner);
    };

    /**
     * Schedules the `callback` on this `runner` for the next animation frame.
     * @private
     * @method _onNextFrame
     * @param {runner} runner
     * @param {function} callback
     * @return {number} frameRequestId
     */
    Runner._onNextFrame = function(runner, callback) {
        if (typeof window !== 'undefined' && window.requestAnimationFrame) {
            runner.frameRequestId = window.requestAnimationFrame(callback);
        } else {
            throw new Error('Matter.Runner: missing required global window.requestAnimationFrame.');
        }

        return runner.frameRequestId;
    };

    /**
     * Cancels the last callback scheduled by `Runner._onNextFrame` on this `runner`.
     * @private
     * @method _cancelNextFrame
     * @param {runner} runner
     */
    Runner._cancelNextFrame = function(runner) {
        if (typeof window !== 'undefined' && window.cancelAnimationFrame) {
            window.cancelAnimationFrame(runner.frameRequestId);
        } else {
            throw new Error('Matter.Runner: missing required global window.cancelAnimationFrame.');
        }
    };

View on GitHub (pinned to acb99b6f87)

Solutions

  1. Run the code in a browser environment where window.requestAnimationFrame exists.
  2. Polyfill requestAnimationFrame before running: global.window = { requestAnimationFrame: cb => setTimeout(cb, 1000/60), cancelAnimationFrame: id => clearTimeout(id) }.
  3. For headless simulation, drive the engine manually with Engine.update(engine, delta) in a setInterval/loop instead of Runner.run.
  4. In jsdom tests, install a RAF polyfill (e.g. requestanimationframe polyfill package or mock).

Example fix

// before (Node.js)
const runner = Matter.Runner.create();
Matter.Runner.run(runner, engine); // throws
// after
Engine.update(engine, 1000 / 60); // manual stepping, or polyfill window.requestAnimationFrame
Defensive patterns

Strategy: fallback

Validate before calling

if (typeof window === 'undefined' || typeof window.requestAnimationFrame !== 'function') {
  // headless: drive manually or polyfill
  globalThis.window = globalThis.window || {};
  window.requestAnimationFrame = window.requestAnimationFrame || (cb => setTimeout(() => cb(Date.now()), 16));
}

Type guard

function hasRaf(env) {
  return typeof env !== 'undefined' && env !== null && typeof env.requestAnimationFrame === 'function';
}

Try / catch

try {
  Matter.Runner.run(runner, engine);
} catch (e) {
  if (String(e.message).includes('requestAnimationFrame')) {
    (function loop(t) { Matter.Engine.update(engine, 1000 / 60); setTimeout(loop, 16); })();
  } else { throw e; }
}

Prevention

When it happens

Trigger: Calling Matter.Runner.run() (which invokes _onNextFrame) in Node.js, in a Web Worker, in a test runner using jsdom without RAF polyfilled, or in a browser so old it lacks requestAnimationFrame.

Common situations: Server-side rendering or running physics simulation headless; unit tests in jsdom (no native RAF); custom engines/environments without a DOM window.

Related errors


AI-assisted analysis of liabru/matter-js@acb99b6f87 (2026-09-02). Data as JSON: /api/errors/76e91b63aaf67615. Report an issue: GitHub.