Simulating Granular Avalanches in Matter.js

This guide explains how to model granular avalanches down an inclined plane using the Matter.js 2D physics engine. By generating an ensemble of polydisperse rigid circular bodies (discs) and placing them on an angled static boundary, you can recreate the micro-mechanical interactions that lead to macroscopic granular flow, jamming transitions, and angle-of-repose phenomena. The following sections walk through setting up the simulation environment, configuring critical contact physics, and executing the simulation code.

1. Setting Up the Matter.js Environment

A granular avalanche model requires an active physics engine, a rendering context, and a continuous update loop. Import Matter.js and initialize the core modules:

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

const engine = Engine.create();
const world = engine.world;

const render = Render.create({
  element: document.body,
  engine: engine,
  options: {
    width: 800,
    height: 600,
    wireframes: false,
    background: '#111'
  }
});

Render.run(render);
const runner = Runner.create();
Runner.run(runner, engine);

2. Creating the Inclined Plane and Basin

The geometry consists of a titled ramp, an optional release gate to hold the material before triggering the avalanche, and a horizontal collection floor at the base.

const rampAngle = Math.PI / 6; // 30 degrees inclination

// Inclined plane
const ramp = Bodies.rectangle(300, 300, 500, 20, {
  isStatic: true,
  angle: rampAngle,
  render: { fillStyle: '#444' }
});

// Flat basin floor at the bottom
const floor = Bodies.rectangle(400, 580, 800, 40, {
  isStatic: true,
  render: { fillStyle: '#333' }
});

// Temporary gate to hold particles before release
const gate = Bodies.rectangle(430, 360, 20, 120, {
  isStatic: true,
  angle: rampAngle - Math.PI / 2,
  render: { fillStyle: '#888' }
});

Composite.add(world, [ramp, floor, gate]);

3. Tuning Granular Material Properties

The behavior of granular media depends heavily on surface friction and restitution (bounciness). Grains in real avalanches exhibit low restitution and moderate-to-high surface friction, which dampens kinetic energy through collisions:

4. Spawning the Granular Discs

Generate a cluster of small circular bodies uphill behind the gate:

const particles = [];
const rows = 15;
const cols = 15;
const baseRadius = 6;

for (let i = 0; i < cols; i++) {
  for (let j = 0; j < rows; j++) {
    // Introduce random radius variation (polydispersity)
    const radius = baseRadius + (Math.random() * 2 - 1) * 1.5;
    
    // Position the stack on the upper section of the slope
    const x = 150 + i * (baseRadius * 2 + 1);
    const y = 100 + j * (baseRadius * 2 + 1);

    const particle = Bodies.circle(x, y, radius, {
      friction: 0.5,
      frictionStatic: 0.8,
      restitution: 0.05,
      density: 0.002,
      render: {
        fillStyle: '#e0a96d'
      }
    });

    particles.push(particle);
  }
}

Composite.add(world, particles);

5. Triggering the Avalanche

To initiate the avalanche, remove the static gate after the particles have settled under standard gravity.

// Remove the gate after 2 seconds to release the flow
setTimeout(() => {
  Composite.remove(world, gate);
}, 2000);

6. Engine Considerations for High Particle Counts

Simulating hundreds of interacting discs can stress the collision detector. To optimize accuracy and stability: