How to Map Three.js Meshes to Matter.js Physics

This guide explains how to synchronize 2D rigid body physics from Matter.js with 3D plane meshes in Three.js. By mapping Matter.js coordinate positions and rotational angles to Three.js mesh transforms on every frame, you can render high-performance 2D physics simulations inside a 3D WebGL scene.

Understanding the Coordinate Systems

To correctly map the two libraries, you must account for the differences in how they handle 2D space:

Using an OrthographicCamera in Three.js makes this conversion direct and eliminates perspective distortion, allowing a 1:1 unit-to-pixel ratio.


Step 1: Configure the Three.js Camera

Set up an OrthographicCamera configured to match the pixel dimensions of your simulation canvas:

const width = window.innerWidth;
const height = window.innerHeight;

const camera = new THREE.OrthographicCamera(
    width / -2, width / 2,
    height / 2, height / -2,
    1, 1000
);
camera.position.z = 10;

Step 2: Create Paired Bodies and Meshes

Create your physics body in Matter.js and corresponding plane geometry in Three.js with identical dimensions, then store a reference between them.

// Matter.js Body
const boxWidth = 100;
const boxHeight = 50;
const body = Matter.Bodies.rectangle(400, 300, boxWidth, boxHeight);
Matter.Composite.add(engine.world, body);

// Three.js Mesh
const geometry = new THREE.PlaneGeometry(boxWidth, boxHeight);
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);

// Track the pair
const physicsObjects = [{ body, mesh }];

Step 3: Convert Coordinates and Update Transforms

In the animation loop, update the physics engine first, then map each body's position and angle to its corresponding mesh.

To convert Matter.js screen coordinates to Three.js centered coordinates:

function animate() {
    requestAnimationFrame(animate);

    // Step the Matter.js physics engine
    Matter.Engine.update(engine, 1000 / 60);

    // Sync mesh transforms to physics bodies
    for (const { body, mesh } of physicsObjects) {
        mesh.position.x = body.position.x - width / 2;
        mesh.position.y = -(body.position.y - height / 2);
        mesh.rotation.z = -body.angle;
    }

    renderer.render(scene, camera);
}

animate();

Using a Perspective Camera Alternative

If you must use a PerspectiveCamera, you cannot use a direct 1:1 pixel mapping. Instead, normalize the Matter.js coordinates to a range of 0 to 1, or use raycasting/visible height formulas (visibleHeight = 2 * Math.tan((vFOV / 2) * Math.PI / 180) * distance) to scale Matter.js pixel coordinates into Three.js world units. For most 2D simulation overlays, an OrthographicCamera remains the simplest and most accurate solution.