How to Create Shockwaves in Matter.js

This article explains how to implement a radial shockwave in Matter.js that propels nearby dynamic bodies outward while ignoring static geometry. By filtering bodies in the physics world, calculating directional vectors from the explosion epicenter, and applying distance-attenuated impulses using Matter.Body.applyForce, you can simulate realistic explosive forces without altering immovable terrain or boundaries.

Understanding the Shockwave Logic

A standard shockwave requires an epicenter, an effective radius, and a maximum force magnitude. To ensure static bodies remain unaffected, you must explicitly check the isStatic flag on each body before applying any force vector.

The process involves four steps:

  1. Identify all bodies within the physics world or a defined bounding area.
  2. Filter out static bodies (body.isStatic === true).
  3. Calculate the distance and direction from the epicenter to each dynamic body.
  4. Scale the force based on proximity and apply it using Matter.Body.applyForce.

Implementation

The following function creates a shockwave at a specified coordinate:

function createShockwave(engine, epicenter, radius, forceMagnitude) {
    const bodies = Matter.Composite.allBodies(engine.world);

    bodies.forEach(body => {
        // Ignore static bodies completely
        if (body.isStatic) return;

        // Calculate vector from shockwave center to the body
        const delta = Matter.Vector.sub(body.position, epicenter);
        const distance = Matter.Vector.magnitude(delta);

        // Check if the body is within the shockwave radius
        if (distance > 0 && distance < radius) {
            // Normalize direction vector
            const direction = Matter.Vector.normalise(delta);

            // Linear falloff: force is strongest at center, zero at edge
            const falloff = 1 - (distance / radius);
            const force = Matter.Vector.mult(direction, forceMagnitude * falloff);

            // Apply force directly to the body's center of mass
            Matter.Body.applyForce(body, body.position, force);
        }
    });
}

Key Considerations

Preventing Zero-Distance Errors

If a dynamic body sits exactly at the epicenter, distance will equal zero. Normalizing a zero-length vector results in NaN coordinates, which corrupts the body's transform matrix. Always verify that distance > 0 before normalizing.

Choosing an Attenuation Model

The example above uses a linear falloff (1 - distance / radius), providing predictable results in 2D space. For a more aggressive, realistic blast, use an inverse-square falloff:

const falloff = 1 / Math.max(1, distance * distance * 0.001);

Performance Optimization with Spatial Queries

If your scene contains hundreds of bodies, iterating over Composite.allBodies() every frame can degrade performance. Instead, use Matter.Query.region() with an axis-aligned bounding box (AABB) to only evaluate bodies within the shockwave's square bounds:

const bounds = {
    min: { x: epicenter.x - radius, y: epicenter.y - radius },
    max: { x: epicenter.x + radius, y: epicenter.y + radius }
};

const nearbyBodies = Matter.Query.region(Matter.Composite.allBodies(engine.world), bounds);

Looping over nearbyBodies rather than the entire world composite reduces vector calculations and ensures optimal frame rates.