How to Create a Wind Force Effect in Matter.js
Creating a wind force effect in Matter.js involves applying a
continuous vector force to bodies within the physics simulation prior to
every engine update. Because Matter.js does not provide a native "wind"
primitive, developers can implement this behavior by leveraging the
engine's event system to loop through active world bodies and apply
directional forces using Matter.Body.applyForce. This guide
demonstrates the most efficient way to apply uniform, variable, and
area-restricted wind across a Matter.js world.
The Core Concept
Matter.js updates its simulation step-by-step. Forces applied to bodies decay immediately after an engine tick, meaning continuous forces like gravity or wind must be reapplied continuously.
To create a global wind effect:
- Listen to the
beforeUpdateevent on the Matter.jsEngine. - Retrieve all bodies currently active in the
worldcomposite. - Exclude static bodies (such as boundaries and floors).
- Apply a force vector to each eligible dynamic body.
Basic Implementation
The standard way to apply a constant horizontal wind blowing to the right is shown in the following implementation:
const { Engine, Render, Runner, Bodies, Composite, Body, Events } = Matter;
// Create engine and world
const engine = Engine.create();
const world = engine.world;
// Define wind properties
const windVector = { x: 0.001, y: 0 }; // Positive x blows to the right
// Listen for updates before the physics step calculates positions
Events.on(engine, 'beforeUpdate', () => {
const bodies = Composite.allBodies(world);
for (let i = 0; i < bodies.length; i++) {
const body = bodies[i];
// Ensure static bodies like walls or floors are unaffected
if (!body.isStatic) {
Body.applyForce(body, body.position, windVector);
}
}
});Because applyForce accelerates a body according to its
mass (\(F = ma\)), lighter objects will
naturally accelerate faster than heavier objects when subjected to the
same force vector.
Adding Dynamic and Gusting Wind
Constant wind can feel artificial. You can simulate wind gusts or turbulence by introducing time-based oscillation using trigonometric functions or pseudo-random values:
Events.on(engine, 'beforeUpdate', (event) => {
const time = event.timestamp * 0.002;
// Create an oscillating wind force with a base direction
const gustStrength = Math.sin(time) * 0.001 + 0.0015;
const dynamicWind = { x: gustStrength, y: 0 };
const bodies = Composite.allBodies(world);
for (let i = 0; i < bodies.length; i++) {
const body = bodies[i];
if (!body.isStatic) {
Body.applyForce(body, body.position, dynamicWind);
}
}
});Restricting Wind to a Specific Area
To simulate localized effects like fans, wind tunnels, or updrafts, define a spatial boundary and check whether a body's position falls within that region before applying the force:
const windZone = {
minX: 100,
maxX: 400,
minY: 0,
maxY: 600
};
const upwardDraft = { x: 0, y: -0.003 };
Events.on(engine, 'beforeUpdate', () => {
const bodies = Composite.allBodies(world);
for (let i = 0; i < bodies.length; i++) {
const body = bodies[i];
if (!body.isStatic) {
const { x, y } = body.position;
// Check if the body is within the wind zone boundaries
const isInZone = x >= windZone.minX &&
x <= windZone.maxX &&
y >= windZone.minY &&
y <= windZone.maxY;
if (isInZone) {
Body.applyForce(body, body.position, upwardDraft);
}
}
}
});Performance Considerations
- Avoid Excessive Allocations: Reusing a single
vector object rather than instantiating a new object inside the
forloop prevents garbage collection spikes at higher frame rates. - Body Filtering: When dealing with large worlds
containing hundreds of static bodies, consider maintaining a separate
array of dynamic bodies to iterate over, rather than traversing
Composite.allBodies(world)on every single tick.