Building Interactive 2D Physics Simulations with Matter.js

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 contact
  • collisionActive: Sustained contact across frames
  • collisionEnd: 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);
});

Tags: Matter.js javascript physics engine 2D Simulation web development

Posted on Sun, 01 Nov 2026 16:51:45 +0000 by colesw