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
- Run the code in a browser environment where window.requestAnimationFrame exists.
- Polyfill requestAnimationFrame before running: global.window = { requestAnimationFrame: cb => setTimeout(cb, 1000/60), cancelAnimationFrame: id => clearTimeout(id) }.
- For headless simulation, drive the engine manually with Engine.update(engine, delta) in a setInterval/loop instead of Runner.run.
- 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
- Only call Runner.run in browser/DOM contexts
- Polyfill requestAnimationFrame when targeting Node, Workers, or jsdom
- For headless simulation, step with Engine.update on your own timer instead of Runner
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.