Create Rolling Boulders Down Inclines in Matter.js

This article explains how to build dynamic rolling boulder hazards that naturally accelerate down winding, multi-tiered slopes using the Matter.js 2D physics engine. You will learn how to configure winding terrain using static body segments or curved vertices, adjust boulder physical properties such as friction and mass for optimal momentum, and implement collision detection to trigger hazard events upon player contact.

1. Setting Up the Physics World

To support heavy, fast-moving objects without tunneling through the floor, initialize your Matter.js engine with adjusted solver iterations and standard gravity pointing downward along the Y-axis.

const { Engine, Render, Runner, Bodies, Composite, Events } = Matter;

const engine = Engine.create({
  positionIterations: 10,
  velocityIterations: 10
});

const world = engine.world;
world.gravity.y = 1.2; // Slightly higher gravity enhances downhill acceleration

2. Constructing the Winding Incline

A winding track requires connected path segments with varying slopes. You can build these using an array of rotated static rectangular bodies or by generating a single static body from SVG path vertices.

To assemble a segmented, winding slope:

function createSegment(x, y, width, height, angle) {
  return Bodies.rectangle(x, y, width, height, {
    isStatic: true,
    angle: angle,
    friction: 0.05,      // Low surface friction allows continuous rolling
    restitution: 0.1     // Minimal bounce keeps the boulder grounded
  });
}

// Create a zigzag descent
const track = [
  createSegment(300, 150, 500, 20, Math.PI / 12),    // Upper slope going right
  createSegment(500, 320, 500, 20, -Math.PI / 10),   // Middle slope going left
  createSegment(300, 500, 500, 20, Math.PI / 8)      // Lower steep slope
];

Composite.add(world, track);

3. Configuring Boulder Physical Properties

Realistic acceleration requires tuning the boulder's mass, friction, and air resistance. Standard Matter.js circular bodies naturally rotate, but specific physical properties ensure they build velocity rather than sliding or stalling.

function spawnBoulder(x, y, radius) {
  const boulder = Bodies.circle(x, y, radius, {
    density: 0.08,
    friction: 0.02,
    frictionAir: 0.0008,
    restitution: 0.15,
    label: 'boulder'
  });

  Composite.add(world, boulder);
  return boulder;
}

const activeBoulder = spawnBoulder(100, 50, 30);

4. Adding Curve Transitions and Banked Corners

Sharp angles between segments can cause the boulder to catch or bounce into the air. Place small, rounded corner segments or circular bumpers at the bends to smoothly transition momentum downward between slope tiers:

const bumper = Bodies.circle(550, 230, 25, {
  isStatic: true,
  friction: 0,
  restitution: 0.4
});
Composite.add(world, bumper);

5. Detecting Collisions with Hazards

Listen for collision events on the engine to register impacts when a rolling boulder hits the player or reaches a despawn boundary.

Events.on(engine, 'collisionStart', (event) => {
  const pairs = event.pairs;

  for (let i = 0; i < pairs.length; i++) {
    const { bodyA, bodyB } = pairs[i];

    if (
      (bodyA.label === 'boulder' && bodyB.label === 'player') ||
      (bodyA.label === 'player' && bodyB.label === 'boulder')
    ) {
      const boulder = bodyA.label === 'boulder' ? bodyA : bodyB;
      const speed = Matter.Vector.magnitude(boulder.velocity);

      if (speed > 2.0) {
        // Trigger hazard damage based on current speed
        console.log(`Player crushed at speed: ${speed}`);
      }
    }
  }
});

Using this approach, downward gravity combined with reduced friction and calculated track banking naturally accelerates boulders down slopes, creating consistent, dynamic hazards driven entirely by the physics simulation.