How to Limit Angles in Matter.js Constraints

Matter.js does not provide native minimum and maximum angle properties for constraints, meaning standard revolute joints will rotate a full 360 degrees freely. To restrict swinging motion, you must actively enforce angle limits yourself. This guide explains how to constrain rotation by clamping body angles programmatically using Matter.js engine update events, as well as an alternative approach using physical stopper bodies.

Understanding Revolute Constraints in Matter.js

A revolute constraint is typically created by pinning a dynamic body to a fixed point in the world or to another body using Constraint.create:

const constraint = Matter.Constraint.create({
    pointA: { x: 400, y: 200 },
    bodyB: swingingBody,
    pointB: { x: 0, y: -50 },
    stiffness: 1,
    length: 0
});

Because the Constraint object only regulates linear distance between points, rotational freedom around the anchor point is determined entirely by the dynamic body's rotational dynamics.

Programmatic Angle Clamping Using Engine Events

The most reliable way to enforce rotational limits is to inspect and clamp the body's angle on every tick of the simulation. This is done by listening to the beforeUpdate event on the Engine.

1. Pinning to a Static World Point

If your body is pinned to a static position, you simply check its absolute angle against minimum and maximum thresholds defined in radians:

const minAngle = -Math.PI / 4; // -45 degrees
const maxAngle = Math.PI / 4;  //  45 degrees

Matter.Events.on(engine, 'beforeUpdate', () => {
    let angle = swingingBody.angle;

    if (angle < minAngle) {
        Matter.Body.setAngle(swingingBody, minAngle);
        Matter.Body.setAngularVelocity(swingingBody, 0);
    } else if (angle > maxAngle) {
        Matter.Body.setAngle(swingingBody, maxAngle);
        Matter.Body.setAngularVelocity(swingingBody, 0);
    }
});

Zeroing the angularVelocity halts the swinging motion immediately once the limit is hit, preventing the momentum from pushing through the clamped threshold on subsequent frames.

2. Pinning Between Two Moving Bodies

When connecting two dynamic bodies (e.g., a forearm connected to an upper arm), the limit must be evaluated relative to the parent body's angle:

Matter.Events.on(engine, 'beforeUpdate', () => {
    const relativeAngle = childBody.angle - parentBody.angle;

    if (relativeAngle < minAngle) {
        Matter.Body.setAngle(childBody, parentBody.angle + minAngle);
        Matter.Body.setAngularVelocity(childBody, parentBody.angularVelocity);
    } else if (relativeAngle > maxAngle) {
        Matter.Body.setAngle(childBody, parentBody.angle + maxAngle);
        Matter.Body.setAngularVelocity(childBody, parentBody.angularVelocity);
    }
});

Matching the child body's angular velocity to the parent's angular velocity prevents jitter when the limit is reached.

3. Adding Bounce at the Limits

To simulate an elastic bounce instead of a dead stop, invert and damp the angular velocity when the limit boundary is breached:

const restitution = 0.4; // Percentage of bounce retained

if (swingingBody.angle < minAngle) {
    Matter.Body.setAngle(swingingBody, minAngle);
    Matter.Body.setAngularVelocity(swingingBody, -swingingBody.angularVelocity * restitution);
} else if (swingingBody.angle > maxAngle) {
    Matter.Body.setAngle(swingingBody, maxAngle);
    Matter.Body.setAngularVelocity(swingingBody, -swingingBody.angularVelocity * restitution);
}

Alternative: Using Physical Barrier Bodies

Another method is placing invisible, static bodies to physically block the path of the swinging body.

  1. Create static bodies using Bodies.rectangle(x, y, width, height, { isStatic: true }).
  2. Position them along the perimeter of the swinging arc where the maximum rotation should occur.
  3. Configure collision filters so the stoppers only interact with the swinging body.

While this approach delegates collision response, friction, and restitution entirely to the physics engine, it requires precise spatial positioning and can sometimes cause high-speed bodies to tunnel or glitch if the colliders are too thin. For precise mechanical joints, the programmatic event-listener method provides greater control and stability.