Fix Matter.js Window Object Errors in Node.js

Running Matter.js in a server-side environment like Node.js often results in a ReferenceError: window is not defined. This occurs because Matter.js includes modules designed for web browsers, specifically its built-in canvas renderer. This article outlines why this error happens and provides actionable methods to resolve it, including isolating the physics engine, replacing the browser renderer, and polyfilling global objects.

Why the Error Occurs

Node.js is a headless JavaScript runtime that lacks browser-specific global APIs such as window, document, and requestAnimationFrame. Matter.js bundles both its core physics engine and a canvas-based visualizer (Matter.Render). When Node.js loads components that attempt to inspect or bind to the browser's display lifecycle via the window object, execution halts immediately with a reference error.

Solution 1: Do Not Use Matter.Render

In a Node.js environment, your application should handle only the physics simulation, not the rendering. The most common cause of the error is importing or instantiating Matter.Render.

Avoid using Render.create() or Render.run() on the server:

const Matter = require('matter-js');

// Deconstruct only physics-related modules
const { Engine, Bodies, Composite } = Matter;

// Create the engine
const engine = Engine.create();

// Create rigid bodies
const box = Bodies.rectangle(400, 200, 80, 80);
const ground = Bodies.rectangle(400, 610, 810, 60, { isStatic: true });

// Add bodies to the world
Composite.add(engine.world, [box, ground]);

Solution 2: Run a Headless Update Loop

Because browser-based helpers rely on window.requestAnimationFrame, run your simulation in Node.js using Engine.update inside a custom loop or via standard Node.js timing mechanisms:

const delta = 1000 / 60; // 60 FPS

setInterval(() => {
  Matter.Engine.update(engine, delta);

  // Access body coordinates for your server logic
  console.log(`Box Position: x=${box.position.x}, y=${box.position.y}`);
}, delta);

For high-precision server-side physics, prefer using process.hrtime() or performance.now() to calculate the exact delta time between updates.

Solution 3: Polyfill the window Object

If you are using an older version of Matter.js or a plugin that references window during module initialization, create a mock window object globally before importing the library.

Add the following polyfill at the entry point of your application before importing Matter.js:

// Polyfill global browser objects
if (typeof window === 'undefined') {
  global.window = {};
  global.document = {
    createElement: () => ({
      getContext: () => null
    })
  };
}

const Matter = require('matter-js');

This mock satisfies initial object checks without importing heavyweight browser emulation libraries, allowing the core physics calculations to run smoothly in headless environments.