Mid-Flight Projectile Dispersion in Matter.js
This article explains how to simulate a dispersing projectile mechanic in the 2D physics engine Matter.js. By launching a primary body and tracking either its flight time, position, or an event trigger, developers can remove the parent entity mid-air and spawn multiple child projectiles with radial velocity vectors. This technique is commonly used in video games for fireworks, multi-stage rockets, and split-shot weapons.
1. Setting Up the Main Projectile
To create a projectile that travels through a physics world, you instantiate a dynamic circular or rectangular body and apply an initial velocity or impulse.
const { Engine, Render, Runner, World, Bodies, Body, Vector, Events } = Matter;
// Create parent projectile
const parentProjectile = Bodies.circle(100, 500, 15, {
density: 0.004,
frictionAir: 0.01,
label: 'parentMunition'
});
// Launch the projectile upward and forward
Body.setVelocity(parentProjectile, { x: 12, y: -18 });
World.add(engine.world, parentProjectile);2. Triggering Mid-Flight Dispersion
The dispersion event can be triggered via a timed delay (using
setTimeout), an altitude check, or when the vertical
velocity changes sign (reaching the trajectory apex). A timer offers
predictable mid-flight detonation:
const flightDuration = 1200; // Milliseconds before separation
setTimeout(() => {
disperseChildProjectiles(parentProjectile, 8, 7);
}, flightDuration);3. Spawning and Dispersing Child Entities
The dispersion function handles removing the original entity from the simulation and creating child particles at the parent's final coordinates. To disperse them outward, calculate radial velocity offsets distributed evenly around a circle (360 degrees) or biased forward along the parent's flight vector.
function disperseChildProjectiles(parent, childCount, dispersionForce) {
// Only disperse if the parent projectile is still in the world
if (!parent || !parent.position) return;
const { x, y } = parent.position;
const parentVelocity = parent.velocity;
// Remove the original body from the simulation
World.remove(engine.world, parent);
const childProjectiles = [];
for (let i = 0; i < childCount; i++) {
// Calculate radial angle for even distribution
const angle = (i / childCount) * (Math.PI * 2);
const child = Bodies.circle(x, y, 5, {
density: 0.002,
frictionAir: 0.02,
label: 'childProjectile'
});
// Combine parent momentum with outward radial force
const radialX = Math.cos(angle) * dispersionForce;
const radialY = Math.sin(angle) * dispersionForce;
Body.setVelocity(child, {
x: parentVelocity.x * 0.5 + radialX,
y: parentVelocity.y * 0.5 + radialY
});
childProjectiles.push(child);
}
// Add all child bodies to the physics engine
World.add(engine.world, childProjectiles);
}4. Cleanup and Performance Considerations
Child projectiles rapidly increase the body count within the engine.
To maintain optimal performance, listen to the engine's
beforeUpdate event or set secondary timers to remove child
bodies after a fixed duration or once they exit the playable screen
bounds.