Simulating Sand Dune Migration in Matter.js
This guide explains how to simulate aerodynamic sand dune migration within the Matter.js 2D physics engine. By combining granular particle physics, realistic angle-of-repose friction, and customized wind vector forces mimicking saltation and creep, you can model emergent barchan and transverse dune behavior in a real-time browser environment.
Physical Principles of Dune Movement
Aeolian (wind-driven) sand transport relies on three primary mechanisms:
- Creep: Heavy grains rolling along the surface due to wind and particle impacts.
- Saltation: Grains lifted into the air by wind shear, traveling downwind, and impacting other grains upon landing.
- Avalanching: Grain accumulation exceeding the natural angle of repose (typically 30° to 34° for dry sand), causing structural collapse on the slip face.
Matter.js handles rigid-body collisions natively, making it well-suited for modeling granular pile mechanics and avalanches through circular rigid bodies. However, aerodynamic forces are not built into the engine and must be applied programmatically each simulation tick.
Configuring Granular Sand Bodies
To simulate cohesive yet granular behavior, configure individual sand particles as small circular bodies with high friction and minimal restitution (bounciness). High friction ensures that grains interlock to form stable slopes up to the angle of repose.
const sandRadius = 3;
const sandOptions = {
friction: 0.8,
frictionStatic: 0.9,
restitution: 0.05,
density: 0.002
};
function createSandGrain(x, y) {
return Matter.Bodies.circle(x, y, sandRadius, sandOptions);
}Modeling Aerodynamic Wind and Saltation
Wind force acts predominantly along the horizontal axis, but saltation requires an aerodynamic lift component caused by air turbulence and surface shear stress.
To apply wind, hook into the beforeUpdate event of the
Matter.js engine:
const prevailingWindVector = { x: 0.00015, y: -0.00003 };
Matter.Events.on(engine, 'beforeUpdate', () => {
const bodies = Matter.Composite.allBodies(engine.world);
bodies.forEach(body => {
// Only affect sand particles, ignoring boundaries
if (body.isStatic) return;
// Apply aerodynamic drag based on position
applyWindForces(body, prevailingWindVector);
});
});Surface Detection and Force Application
In nature, wind only acts upon the surface layer of sand. If you apply wind forces globally to all bodies, the entire dune will slide uniformly as a block instead of migrating organically.
You can restrict wind forces to surface grains using one of two methods:
Method 1: Grid-Based Height Map Check
Calculate the highest active particle in vertical vertical slices (columns) along the x-axis:
function applyWindForces(body, wind) {
// Only apply forces to exposed grains near the surface
const surfaceThresholdY = getSurfaceYAt(body.position.x);
if (body.position.y <= surfaceThresholdY + sandRadius * 2) {
// Add pseudo-random turbulence to simulate saltation lift
const turbulence = (Math.random() - 0.5) * 0.00005;
Matter.Body.applyForce(body, body.position, {
x: wind.x * body.mass,
y: (wind.y + turbulence) * body.mass
});
}
}Method 2: Neighbor Density Checks
Count how many neighboring bodies surround a given grain using
Matter.Query.point or spatial hashes. Bodies surrounded on
all sides are subterranean and should receive zero wind force, while
bodies with few neighbors above them receive full wind shear.
Emergent Dune Dynamics
Once tuned, the simulation produces realistic self-organizing dune morphologies:
- Windward Slope (Stoss): Wind pushes surface grains up the gentle windward slope via saltation and creep.
- Crest and Separation: As sand reaches the crest, it drops into the aerodynamic shelter (wake zone) on the leeward side where wind shear drops to zero.
- Leeward Slope (Slip Face): Sand accumulates at the crest until the slope angle exceeds the angle of repose. The inter-particle friction in Matter.js fails under gravity, triggering a localized avalanche down the slip face.
- Forward Migration: This continuous transfer of sand from the upwind side to the downwind slope causes the entire dune structure to migrate in the direction of the prevailing wind.
Optimization Strategies
Because granular simulations require hundreds or thousands of bodies, maintain 60 FPS performance with these optimizations:
- Particle Sleep: Enable
enableSleeping: trueon the Matter.js engine so that subterranean particles within the dune enter a low-overhead rest state until disturbed by surface avalanches. - Hybrid Modeling: For large-scale simulations, represent the interior core of the dune as a static or deformable polygon, and use Matter.js circular bodies exclusively for the migrating active surface layer.