Physics engines simulate real-world mechanical interactions within digital environments. For web-based 2D physics, Matter.js provides a lightweight yet robust JavaScript solution capable of handling rigid body dynamics, collision detection, and constraint-based interactions directly in the browser.
Unlike 3D alternatives such as Cannon.js or Ammo.js, Matter.js focuses specifical on two-dimensional space, offering optimized performance for interactive web applications, games, and simulations.
Core Architecture
Understanding Matter.js requires familiarity with several fundamental components:
| Module | Function |
|---|---|
| Engine | The central orchestrator managing physics calculations, gravity, and temporal updates |
| World | The container holding all physical entities within a simulation |
| Body | Rigid physical objects possessing mass, velocity, and geometry |
| Composite | Hierarchical containers grouping multiple bodies and constraints |
| Constraint | Connections limiting relative motion between bodies (springs, joints, rods) |
| Runner | The execution loop driving continuous simulation updates |
| Render | Visualization layer translating physics state into canvas graphics |
The Engine computes physical interacsions while the Runner advances simulation time. Bodies represent tangible objects, and Composites organize them into manageable structures. Constraints create mechanical relationships, and the Render module provides immediate visual feedback during development.
Installation
Include Matter.js via CDN:
<script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.19.0/matter.min.js"></script>
Or install via npm:
npm install matter-js
Minimal Working Example
The following implementation creates a basic scene with falling blocks and a static ground plane:
const { Engine, Render, Runner, Bodies, Composite } = Matter;
// Initialize physics engine
const simulationEngine = Engine.create();
// Configure visualization
const viewport = Render.create({
element: document.getElementById('scene'),
engine: simulationEngine,
options: {
width: 800,
height: 600,
wireframes: false
}
});
// Create dynamic boxes
const crate = Bodies.rectangle(400, 200, 80, 80);
const plank = Bodies.rectangle(450, 50, 80, 80, {
angle: Math.PI / 6
});
// Create static ground
const terrain = Bodies.rectangle(400, 580, 810, 60, {
isStatic: true,
render: { fillStyle: '#2c3e50' }
});
// Populate world
Composite.add(simulationEngine.world, [crate, plank, terrain]);
// Start simulation
Render.run(viewport);
const loop = Runner.create();
Runner.run(loop, simulationEngine);
This establishes the standard workflow: engine initialization, body creation, world population, and execution loop activation.
Visualization Configuration
The built-in renderer supports extensive customization beyond default wireframe display.
Canvas Dimensions
Explicitly define viewport size through options:
const viewport = Render.create({
element: container,
engine: simulationEngine,
options: {
width: 1024,
height: 768
}
});
Solid Rendering
Disable wireframe mode to display filled geometries:
options: {
wireframes: false,
background: '#f0f0f0'
}
Rigid Body Construction
Matter.js generates physical entities through factory methods accepting position vectors and dimensional parameters.
Rectangles
const platform = Bodies.rectangle(x, y, width, height, options);
Coordinates specify the centroid. For a 100×100 rectangle positioned with its top-left corner at (0,0), set x=50 and y=50.
Circles
const sphere = Bodies.circle(centerX, centerY, radius, {
restitution: 0.8
});
Polygons
Regular polygons use the polygon method:
const heptagon = Bodies.polygon(300, 200, 7, 40); // 7 sides, 40px radius
Trapezoids
Asymmetric shapes accept slope parameters:
// Slope < 1: top narrower than bottom
// Slope = 0: rectangle
// Slope >= 1: triangle
const wedge = Bodies.trapezoid(200, 200, 100, 80, 0.5);
Arbitrary Vertices
Complex polygons require vertex arrays:
const vertices = [
{ x: 0, y: 0 },
{ x: 60, y: 0 },
{ x: 80, y: 40 },
{ x: 40, y: 80 },
{ x: 0, y: 60 }
];
const crystal = Bodies.fromVertices(150, 150, vertices);
Physical Properties and Styling
Body configurations accept both physical parameters and rendering instructions.
Visual Attributes
const coloredBlock = Bodies.rectangle(200, 200, 100, 100, {
render: {
fillStyle: '#e74c3c',
strokeStyle: '#c0392b',
lineWidth: 3,
opacity: 0.9
}
});
Textures
Sprite mapping applies images to bodies:
const texturedCrate = Bodies.rectangle(200, 200, 120, 120, {
render: {
sprite: {
texture: './crate.png',
xScale: 0.5,
yScale: 0.5,
xOffset: 0,
yOffset: 0
}
}
});
Dynamics
Air Resistance: Simulates atmospheric drag affecting terminal velocity.
const parachute = Bodies.rectangle(100, 100, 60, 60, {
frictionAir: 0.05 // Higher values increase drag
});
Elasticity: Restitution coefficients control bounce behavior (0 = inelastic, 1 = perfectly elastic).
const bouncyBall = Bodies.circle(200, 100, 40, {
restitution: 1.2 // Values > 1 add energy (unrealistic but useful for games)
});
Mass: Influences collision response and inertia.
const anvil = Bodies.rectangle(300, 100, 60, 60, {
mass: 50, // Overrides density-based calculation
restitution: 0.1
});
Static Bodies: Immovable objects for terrain or obstacles.
const wall = Bodies.rectangle(400, 600, 800, 40, {
isStatic: true
});
Composite Structures
The Composites module generates organized object collections.
Stacks
Grid arrangements simplify creating walls or piles:
const brickWall = Composites.stack(50, 50, 8, 4, 5, 5, (x, y) => {
return Bodies.rectangle(x, y, 40, 20, {
render: { fillStyle: '#d35400' }
});
});
Composite.add(simulationEngine.world, brickWall);
The callback receives calculated grid positions, allowing uniform property application across all generated bodies.
Transformations apply to entire composites:
// Rotate entire structure 30 degrees around pivot
Composite.rotate(brickWall, Math.PI / 6, { x: 200, y: 200 });
Constraints and Joints
Constraints create mechanical relationships between bodies, functioning as springs, rods, or pivot points.
Basic Constraint
A simple connection maintaining distance between entities:
const rod = Constraint.create({
bodyA: pendulumArm,
bodyB: weight,
length: 120,
stiffness: 0.8
});
Setting length: 0 creates a rigid weld. The stiffness parameter (0-1) determines elasticity.
Seesaw Implementation
const fulcrum = Bodies.rectangle(400, 350, 40, 120, {
isStatic: true,
collisionFilter: { group: -1 }
});
const beam = Bodies.rectangle(400, 300, 300, 20, {
collisionFilter: { group: -1 }
});
const pivot = Constraint.create({
bodyA: fulcrum,
bodyB: beam,
pointA: { x: 0, y: -50 },
pointB: { x: 0, y: 0 },
length: 0,
stiffness: 1
});
Composite.add(simulationEngine.world, [fulcrum, beam, pivot]);
Mouse Interaction
Enable direct manipulation through MouseConstraint:
const pointer = Mouse.create(viewport.canvas);
const grabConstraint = MouseConstraint.create(simulationEngine, {
mouse: pointer,
constraint: {
stiffness: 0.2,
render: { visible: false }
}
});
Composite.add(simulationEngine.world, grabConstraint);
This allows clicking and dragging bodies within the simulation.
Event Handling
Matter.js emits events for collisions and input state changes.
Collision Detection
Events.on(simulationEngine, 'collisionStart', (event) => {
event.pairs.forEach((pair) => {
const { bodyA, bodyB } = pair;
console.log(`Impact detected between ${bodyA.label} and ${bodyB.label}`);
});
});
Available collision events include:
collisionStart: Initial contactcollisionActive: Sustained contact across framescollisionEnd: Separation
Input Events
Mouse constraints emit interaction events:
Events.on(grabConstraint, 'mousedown', (event) => {
console.log('Interaction initiated');
});
Events.on(grabConstraint, 'startdrag', (event) => {
console.log('Grabbing body:', event.body);
});
Events.on(grabConstraint, 'enddrag', (event) => {
console.log('Released body:', event.body);
});