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.
- Create static bodies using
Bodies.rectangle(x, y, width, height, { isStatic: true }). - Position them along the perimeter of the swinging arc where the maximum rotation should occur.
- 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.