Limit Velocity in Matter.js with Matter.Common.clamp

In fast-paced 2D physics simulations, rigid bodies can achieve extreme velocities that lead to "tunneling" through boundaries or simulation crashes caused by floating-point overflow. This article demonstrates how to stabilize your Matter.js physics engine by using the built-in Matter.Common.clamp function to enforce safe upper and lower bounds on body velocity vectors during the simulation update loop.

Why Clamp Velocity Vectors?

Matter.js relies on discrete time-stepping. When an object moves too fast in a single step, it can pass entirely through static colliders without triggering a collision. Restricting velocity vector components ensures objects stay within predictable thresholds, maintaining simulation stability and preventing physics glitches.

Understanding Matter.Common.clamp

Matter.js provides a helper method located at Matter.Common.clamp. Its signature is:

Matter.Common.clamp(value, min, max);

It accepts an input value and returns:

Method 1: Clamping Independent Axes (X and Y)

The simplest approach is clamping the x and y components of a body's velocity separately. This creates a square boundary on maximum speed.

const { Engine, Events, Body, Common } = Matter;

const MAX_SPEED_X = 15;
const MAX_SPEED_Y = 15;

Events.on(engine, 'beforeUpdate', () => {
    const bodies = Composite.allBodies(engine.world);

    for (let i = 0; i < bodies.length; i++) {
        const body = bodies[i];

        if (body.isStatic) continue;

        const clampedX = Common.clamp(body.velocity.x, -MAX_SPEED_X, MAX_SPEED_X);
        const clampedY = Common.clamp(body.velocity.y, -MAX_SPEED_Y, MAX_SPEED_Y);

        Body.setVelocity(body, { x: clampedX, y: clampedY });
    }
});

Hooking into the beforeUpdate event ensures the velocity is restricted before the physics solver integrates positions and calculates collisions for that frame.

Method 2: Clamping Vector Magnitude (Preserving Direction)

Clamping axes independently can distort the movement direction when diagonal velocities exceed the threshold. To maintain the exact direction of motion, clamp the overall magnitude (speed) of the velocity vector:

const { Events, Body, Common, Vector } = Matter;

const MAX_SPEED = 20;

Events.on(engine, 'beforeUpdate', () => {
    const bodies = Composite.allBodies(engine.world);

    for (let i = 0; i < bodies.length; i++) {
        const body = bodies[i];

        if (body.isStatic) continue;

        const currentSpeed = Vector.magnitude(body.velocity);

        if (currentSpeed > 0) {
            // Clamp speed between 0 and MAX_SPEED
            const clampedSpeed = Common.clamp(currentSpeed, 0, MAX_SPEED);

            // Rescale velocity vector to clamped speed
            if (clampedSpeed !== currentSpeed) {
                const normalized = Vector.normalise(body.velocity);
                Body.setVelocity(body, Vector.mult(normalized, clampedSpeed));
            }
        }
    }
});

Best Practices