Matter.js Bouncing Grenade Projectile Simulation

Simulating a realistic bouncing grenade in Matter.js requires configuring the physical properties of both the projectile and the terrain, properly applying launch forces, and managing collision restitution. This guide covers how to set up the Matter.js physics engine, configure the restitution (bounciness) and friction values to mimic a heavy explosive canister, apply initial velocity and angular spin, and implement progressive damping so the grenade settles naturally rather than bouncing perpetually.

Understanding Restitution in Matter.js

In Matter.js, the restitution property defines how much kinetic energy is retained after a collision, with values ranging from 0 (completely inelastic) to 1 (completely elastic).

A critical detail of Matter.js is its collision resolution formula: by default, when two bodies collide, the engine resolves the bounce using Math.max(bodyA.restitution, bodyB.restitution). Because of this, giving the ground a restitution of 0.8 will cause every object to bounce high, regardless of its own properties. For a realistic simulation, keep the ground's restitution low (between 0.1 and 0.3) and define the primary bounce characteristics on the grenade itself.

Configuring the Grenade Body

A military fragmentation or smoke grenade is a dense, relatively heavy object. It should bounce significantly on its first impact but rapidly bleed kinetic energy due to metal deformation, ground friction, and mass.

Set the grenade body with the following properties:

const grenade = Matter.Bodies.circle(startX, startY, 10, {
    density: 0.004,        // Heavier than default to resist light forces
    restitution: 0.45,     // Moderate bounce
    friction: 0.8,         // High surface friction to induce rolling
    frictionAir: 0.005,    // Low air resistance for a parabolic arc
    frictionStatic: 1.0,   // Prevents sliding once settled
    render: {
        fillStyle: '#4B5320' // Military olive drab
    }
});

Setting Up the Ground

Define the ground as a static body with minimal bounciness to ensure the grenade controls the bounce dynamic:

const ground = Matter.Bodies.rectangle(400, 590, 810, 30, {
    isStatic: true,
    restitution: 0.2,
    friction: 0.9
});

Launching the Grenade

Launch the grenade by applying an initial velocity vector combined with angular velocity (spin). Applying spin enhances realism because the grenade will kick forward or backward depending on the orientation of its collision with the terrain:

// Launch with an angle and power
const launchAngle = -Math.PI / 4; // 45 degrees upward
const launchSpeed = 15;

Matter.Body.setVelocity(grenade, {
    x: Math.cos(launchAngle) * launchSpeed,
    y: Math.sin(launchAngle) * launchSpeed
});

// Add rotational spin
Matter.Body.setAngularVelocity(grenade, 0.15);

Simulating Realistic Energy Loss on Impact

Pure restitution calculations often leave projectiles feeling floaty or rubbery. Real grenades lose their bounciness exponentially after each hit. You can implement progressive energy loss using the collisionStart event:

Matter.Events.on(engine, 'collisionStart', (event) => {
    event.pairs.forEach((pair) => {
        const body = pair.bodyA === grenade ? pair.bodyA : (pair.bodyB === grenade ? pair.bodyB : null);

        if (body) {
            // Decay restitution with each bounce
            body.restitution *= 0.6;

            // Reduce horizontal slide on hard impacts
            Matter.Body.setVelocity(body, {
                x: body.velocity.x * 0.7,
                y: body.velocity.y
            });

            // Cut off tiny micro-bounces to let the body sleep
            if (Math.abs(body.velocity.y) < 1.5) {
                body.restitution = 0;
            }
        }
    });
});

This progressive decay ensures the first impact absorbs the majority of the velocity, producing a short second hop and a quick transition into a roll, matching the real-world behavior of heavy ordnance.