Model Oil Spill Containment Booms in Matter.js
This article explains how to simulate oil spill containment using floating boom barriers within the Matter.js 2D physics engine. It details how to approximate fluid behavior using particle clusters, construct articulated physical boom barriers using constraint chains, and apply custom environmental forces like buoyancy and hydrodynamic drag to achieve realistic containment dynamics.
1. Representing the Oil Slick with Particles
Because Matter.js is a rigid-body physics engine rather than a computational fluid dynamics (CFD) solver, liquid slicks are best simulated using a system of discrete, interacting circular particles.
Create a cluster of small circles with low friction and zero restitution (bounciness) to mimic viscous crude oil:
const createOilParticle = (x, y) => {
return Matter.Bodies.circle(x, y, 6, {
density: 0.0008, // Slightly less dense than standard water approximation
friction: 0.05,
frictionAir: 0.03, // Simulates fluid resistance
restitution: 0.0,
collisionFilter: {
category: 0x0002,
mask: 0x0001 | 0x0002 // Collides with booms and other oil particles
},
render: {
fillStyle: '#1a1a1a'
}
});
};To emulate surface tension and cohesive fluid behavior, apply a weak attraction force between neighboring particles inside an update loop.
2. Constructing the Boom Barrier
Real containment booms consist of connected buoyant segments that flex with water movement while blocking surface pollutants. In Matter.js, model this as a chain of rectangular bodies linked together by distance constraints.
const createBoom = (startX, startY, segments, segmentWidth, segmentHeight) => {
const boomElements = [];
let previousBody = null;
for (let i = 0; i < segments; i++) {
const x = startX + i * segmentWidth;
const body = Matter.Bodies.rectangle(x, startY, segmentWidth, segmentHeight, {
density: 0.0012,
frictionAir: 0.05,
collisionFilter: {
category: 0x0001,
mask: 0x0002 // Collides with oil
},
render: { fillStyle: '#ffaa00' }
});
boomElements.push(body);
if (previousBody) {
const constraint = Matter.Constraint.create({
bodyA: previousBody,
pointA: { x: segmentWidth / 2, y: 0 },
bodyB: body,
pointB: { x: -segmentWidth / 2, y: 0 },
stiffness: 0.9,
damping: 0.1
});
boomElements.push(constraint);
}
previousBody = body;
}
return boomElements;
};Anchor the ends of the boom by attaching static constraints to the first and last segments, or attach them to static anchor bodies positioned at deployment coordinates.
3. Simulating Buoyancy and Currents
Water level, current velocity, and buoyancy must be applied
programmatically using Matter.js engine events. Hook into the
beforeUpdate event to apply vertical buoyancy and lateral
current forces.
Matter.Events.on(engine, 'beforeUpdate', () => {
const waterLevel = 400; // Y-coordinate of the water surface
const currentForce = { x: 0.00005, y: 0 }; // Directional drift
const allBodies = Matter.Composite.allBodies(engine.world);
allBodies.forEach(body => {
// Apply current to all floating objects
Matter.Body.applyForce(body, body.position, currentForce);
// Apply buoyant force if submerged below the water line
if (body.position.y > waterLevel) {
const submergedDepth = body.position.y - waterLevel;
const buoyancyMagnitude = body.mass * 0.001 * submergedDepth;
Matter.Body.applyForce(body, body.position, {
x: 0,
y: -Math.min(buoyancyMagnitude, body.mass * 0.02)
});
}
});
});4. Preventing Particle Tunneling
Small particles under pressure can clip through boom barriers if velocities spike. To maintain strict containment integrity:
- Sub-stepping: Increase the engine update frequency by executing multiple smaller engine updates per animation frame instead of a single large delta.
- Segment Thickness: Ensure boom barrier rectangles have sufficient thickness on the non-colliding axis to prevent high-velocity particles from leaping through geometry in a single frame.
- Collision Masking: Explicitly define collision bitmasks so oil particles do not calculate unnecessary interactions with underwater terrain or static boundaries outside the containment zone.