Applying Directional Wind Gusts in Matter.js
This guide explains how to simulate realistic, directional wind gusts
with randomized noise parameters in Matter.js. Because Matter.js does
not feature a native environmental force engine, you must calculate
dynamic vector forces and apply them to target bodies during the physics
update cycle. By leveraging the beforeUpdate event
alongside procedural noise functions like Perlin noise or sinusoidal
wave combinations, you can generate continuous, organic airflow with
unpredictable bursts and turbulence.
1. Setting Up the Wind Loop
To affect bodies consistently, wind must be computed and applied
before every physics step. Hook into the Matter.js engine update loop
using the Events.on method with the
beforeUpdate event.
const { Engine, Events, Body, Vector } = Matter;
const engine = Engine.create();
Events.on(engine, 'beforeUpdate', (event) => {
const time = event.timestamp * 0.001; // Current simulation time in seconds
applyWindToBodies(engine.world.bodies, time);
});2. Generating Randomized Noise Parameters
True random values (Math.random()) create jittery,
unnatural motion. For realistic wind gusts, use coherent noise—such as
Simplex noise or multi-octave sine approximations—to vary wind speed and
direction smoothly over time.
Below is an implementation using layered sine waves to approximate continuous 1D noise without external dependencies:
function getNoise(time, frequency, seed = 0) {
return (
Math.sin(time * frequency + seed) * 0.5 +
Math.sin(time * frequency * 2.3 + seed * 1.5) * 0.3 +
Math.sin(time * frequency * 5.1 + seed * 2.0) * 0.2
);
}3. Calculating the Directional Wind Vector
Combine a base directional vector with noise-driven offsets for gust intensity and directional variance.
const windConfig = {
baseDirection: { x: 1, y: -0.1 }, // Generally blowing right, slightly upward
baseStrength: 0.0005,
gustFrequency: 0.8,
gustScale: 0.002,
turbulenceFrequency: 2.5,
turbulenceScale: 0.3 // Radians of angle variation
};
function calculateWindForce(time) {
// 1. Calculate gust magnitude (0 to 1)
const gustNoise = Math.max(0, getNoise(time, windConfig.gustFrequency, 42));
const totalStrength = windConfig.baseStrength + (gustNoise * windConfig.gustScale);
// 2. Calculate directional turbulence (angle deviation)
const angleOffset = getNoise(time, windConfig.turbulenceFrequency, 99) * windConfig.turbulenceScale;
const baseAngle = Math.atan2(windConfig.baseDirection.y, windConfig.baseDirection.x);
const finalAngle = baseAngle + angleOffset;
// 3. Create final directional force vector
return {
x: Math.cos(finalAngle) * totalStrength,
y: Math.sin(finalAngle) * totalStrength
};
}4. Applying the Force to Bodies
Iterate over the active bodies in the simulation, ensuring static bodies are ignored. Scale the force based on the surface area or mass of the body if you want larger objects to experience proportional drag.
function applyWindToBodies(bodies, time) {
const windForce = calculateWindForce(time);
for (let i = 0; i < bodies.length; i++) {
const body = bodies[i];
// Skip static bodies and sleeping bodies
if (body.isStatic || body.isSleeping) continue;
// Apply aerodynamic drag factor based on mass or surface area
const dragFactor = body.mass;
const scaledForce = {
x: windForce.x * dragFactor,
y: windForce.y * dragFactor
};
// Apply force at the center of mass
Body.applyForce(body, body.position, scaledForce);
}
}5. Adding Spatial Noise (Optional)
If your canvas is large, wind forces should vary by location as well as time. Incorporate the body's coordinates into the noise function:
function calculateSpatialWindForce(time, position) {
const spatialSeed = position.x * 0.005 + position.y * 0.002;
const localGust = Math.max(0, getNoise(time + spatialSeed, windConfig.gustFrequency, 42));
const strength = windConfig.baseStrength + (localGust * windConfig.gustScale);
return {
x: windConfig.baseDirection.x * strength,
y: windConfig.baseDirection.y * strength
};
}Passing body.position into this function before invoking
Body.applyForce ensures that wind gusts roll across the
screen across different positions rather than hitting all bodies
simultaneously.