Simulate Pyroclastic Flows in Matter.js

This guide explains how to model volcanic pyroclastic flows descending sloped terrain using the Matter.js 2D physics engine. By combining continuous particle emission, tailored material properties, custom forces for fluidization and aerodynamic drag, and static chain-segment terrain, you can effectively reproduce the dual behavior of heavy rock debris and turbulent ash clouds characteristic of pyroclastic surges.

1. Setting Up the Sloped Terrain

Matter.js models terrain using static rigid bodies. A pyroclastic flow requires an uneven, inclined mountain face rather than a flat plane.

To create smooth or rugged slopes, assemble static rectangular segments or create a single concave polygon using vertex points:

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

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

// Create an inclined surface using static bodies
const slopeSegments = [
  Bodies.rectangle(200, 150, 300, 20, { isStatic: true, angle: Math.PI / 6 }),
  Bodies.rectangle(450, 320, 300, 20, { isStatic: true, angle: Math.PI / 4 }),
  Bodies.rectangle(700, 500, 400, 20, { isStatic: true, angle: Math.PI / 8 }),
  Bodies.rectangle(950, 580, 200, 20, { isStatic: true, angle: 0 }) // Runout zone
];

// Apply rough friction to the ground
slopeSegments.forEach(segment => {
  segment.friction = 0.8;
  Composite.add(world, segment);
});

2. Modeling Flow Particles

A pyroclastic flow consists of two primary components: a dense basal granular flow (blocks and lapilli) and a dilute ash cloud (gas and fine ash). Represent these elements with two types of dynamic circle bodies.

Dense Clasts (Basal Layer)

Ash and Gas Particles (Surge Layer)

function createDenseClast(x, y) {
  return Bodies.circle(x, y, 6, {
    density: 0.005,
    friction: 0.3,
    restitution: 0.1,
    render: { fillStyle: '#3a3a3a' }
  });
}

function createAshParticle(x, y) {
  return Bodies.circle(x, y, 12, {
    density: 0.0002,
    frictionAir: 0.05,
    friction: 0.01,
    restitution: 0.4,
    collisionFilter: { group: -1 }, // Minimizes particle-on-particle jams
    render: { fillStyle: 'rgba(150, 150, 150, 0.5)' }
  });
}

3. Implementing the Particle Emitter

Continuous generation replicates the sustained collapse of a volcanic column or lava dome. Spawn particles at the top of the slope with an initial forward momentum.

const particles = [];
const maxParticles = 600;

function emitFlow() {
  if (particles.length >= maxParticles) return;

  const originX = 70;
  const originY = 60;

  // Emit both types of particles
  const isAsh = Math.random() > 0.4;
  const particle = isAsh 
    ? createAshParticle(originX, originY) 
    : createDenseClast(originX, originY);

  // Apply initial ejection velocity down the slope
  Body.setVelocity(particle, {
    x: 4 + Math.random() * 2,
    y: 2 + Math.random() * 2
  });

  particles.push(particle);
  Composite.add(world, particle);
}

4. Simulating Fluidization and Aerodynamic Effects

Real pyroclastic flows achieve high speeds over low slopes due to "fluidization"—trapped high-pressure gas reduces friction between solid fragments. Matter.js does not calculate internal gas pressures natively, but you can approximate these forces inside the beforeUpdate engine event.

Fluidization Lift

Apply a slight upward buoyant force to mimic gas escaping between clasts:

Matter.Events.on(engine, 'beforeUpdate', () => {
  emitFlow();

  for (let i = particles.length - 1; i >= 0; i--) {
    const p = particles[i];

    // Remove particles that travel out of bounds
    if (p.position.y > 700 || p.position.x > 1100) {
      Composite.remove(world, p);
      particles.splice(i, 1);
      continue;
    }

    // Thermal lift for ash particles
    if (p.density < 0.001) {
      Body.applyForce(p, p.position, {
        x: (Math.random() - 0.5) * 0.0001,
        y: -0.00015 * p.mass // Counteracts downward gravity
      });
    }

    // Basal fluidization (reduces effective weight on the ground)
    if (p.density >= 0.001 && p.velocity.x > 1) {
      Body.applyForce(p, p.position, {
        x: 0,
        y: -0.00005 * p.mass
      });
    }
  }
});

5. Visual Enhancement

To make discrete Matter.js circles resemble an actual turbulent flow:

  1. Canvas Compositing: Set the rendering context to use overlapping blend modes like lighter or soft transparency so that overlapping ash circles blur into a cohesive cloud.
  2. Radial Sizing: Incrementally increase the radius of ash bodies using Body.scale() as they travel downward to mimic turbulent mixing with cold ambient air and volumetric expansion.