How to Fix Snapping Constraints in Matter.js

Constraints that snap, vibrate, or explode wildly in Matter.js usually stem from numerical instability within the constraint solver. This article covers the primary causes of constraint instability—including conflicting collision responses, improper stiffness settings, low solver iterations, and extreme mass ratios—along with direct code adjustments to stabilize your physics simulation immediately.

1. Disable Collisions Between Constrained Bodies

The most common reason constraints snap violently is a physics loop: the constraint pulls two bodies together, but their collision boundaries push them apart. This creates an exponential accumulation of kinetic energy.

To prevent connected bodies from colliding with each other, assign them to the same negative collision group:

const group = Matter.Body.nextGroup(true);

const bodyA = Matter.Bodies.circle(100, 100, 20, { collisionFilter: { group: group } });
const bodyB = Matter.Bodies.circle(100, 150, 20, { collisionFilter: { group: group } });

const constraint = Matter.Constraint.create({
    bodyA: bodyA,
    bodyB: bodyB,
    stiffness: 0.9
});

2. Increase Engine Iterations

By default, Matter.js balances performance and accuracy with low solver iterations. When multiple constraints are chained or experience high loads, the default iterations fail to converge on a valid solution, leading to rubber-banding and snapping.

Increase the solver iterations on the engine configuration:

const engine = Matter.Engine.create({
    positionIterations: 10,  // Default is 6
    velocityIterations: 8,   // Default is 4
    constraintIterations: 4  // Default is 2
});

Increasing constraintIterations gives the solver more passes per frame to reconcile conflicting forces.

3. Adjust Stiffness and Add Damping

A constraint with a stiffness value of 1 acts as a perfectly rigid rod. If outside forces pull the bodies past their defined length, a completely stiff constraint applies an instantaneous, massive correcting impulse.

Lowering stiffness slightly or introducing damping smooths out extreme force spikes:

const constraint = Matter.Constraint.create({
    bodyA: bodyA,
    bodyB: bodyB,
    length: 100,
    stiffness: 0.8, // Slightly softer than rigid 1.0
    damping: 0.1    // Absorbs kinetic oscillation
});

4. Explicitly Define Rest Length

If you do not pass a length property to Constraint.create(), Matter.js calculates the rest length automatically based on the bodies' positions at initialization. If bodies spawn overlapping or too far apart, the initial calculated length may be incorrect, leading to a sudden snap on frame one.

Always define length explicitly:

const constraint = Matter.Constraint.create({
    bodyA: bodyA,
    pointA: { x: 0, y: 0 },
    bodyB: bodyB,
    pointB: { x: 0, y: 0 },
    length: 80
});

5. Balance Mass Ratios

If body A has a mass of 1 and body B has a mass of 100, the constraint solver must apply massive displacement to body A to balance the movement of body B. This mass disparity causes lightweight bodies to whip around unstably.

Ensure connected bodies have comparable masses, or increase the mass of the lighter object:

Matter.Body.setMass(bodyA, 10);
Matter.Body.setMass(bodyB, 15);

6. Stabilize the Engine Delta Time

Fluctuating frame rates (delta spikes) cause the physics engine to simulate large steps in a single frame. Large steps cause bodies to tunnel or overshoot constraint limits, triggering an explosive correction.

Run the engine with a fixed time step rather than variable frame deltas:

// Example inside a requestAnimationFrame loop
const fixedDelta = 1000 / 60; // 60 FPS
Matter.Engine.update(engine, fixedDelta);