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:
- Matter.js: The origin
(0, 0)is at the top-left of the viewport. The X-axis extends to the right, and the Y-axis extends downward. Rotation values are in radians and increase clockwise. - Three.js: In standard setup, the origin
(0, 0, 0)is at the center of the screen. The X-axis extends to the right, and the Y-axis extends upward. Rotation around the Z-axis increases counterclockwise.
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:
- Mesh X:
body.position.x - (width / 2) - Mesh Y:
-(body.position.y - (height / 2)) - Mesh Rotation (Z):
-body.angle
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.