How to Create Breakable Welds in Matter.js

Creating breakable welds in Matter.js allows you to simulate destructible structures, brittle joints, and breakaway vehicle parts that snap apart under severe impacts. Because Matter.js does not have an out-of-the-box breakable constraint property, you must simulate this behavior by combining rigid constraints with collision event listeners to evaluate impact forces and remove the constraint from the physics world when a predefined threshold is exceeded.

Setting Up Rigid Welds

In Matter.js, a weld is represented by a Constraint with a stiffness value close to 1 and an appropriate length (typically 0 for direct body-to-body attachments).

const { Bodies, Constraint, Composite } = Matter;

// Create two bodies to weld together
const bodyA = Bodies.rectangle(400, 300, 80, 40);
const bodyB = Bodies.rectangle(400, 340, 80, 40);

// Create the rigid weld constraint
const weld = Constraint.create({
    bodyA: bodyA,
    bodyB: bodyB,
    pointA: { x: 0, y: 20 },
    pointB: { x: 0, y: -20 },
    stiffness: 1,
    length: 0
});

// Custom property to define breaking force and status
weld.breakForce = 15; 
weld.isBroken = false;

Composite.add(engine.world, [bodyA, bodyB, weld]);

Detecting Collision Forces

Constraints in Matter.js do not natively compute dynamic stress vectors internally. Instead, the most reliable way to snap a weld on a heavy impact is to monitor collision events using Events.on(engine, 'collisionStart', handler).

During a collision, you measure the impact force by evaluating the relative velocity and mass of the colliding bodies, or by checking the impulse generated across the collision pairs.

Matter.Events.on(engine, 'collisionStart', (event) => {
    const pairs = event.pairs;

    for (let i = 0; i < pairs.length; i++) {
        const pair = pairs[i];
        const { bodyA, bodyB } = pair;

        // Calculate relative speed between the two colliding bodies
        const relativeVelocity = {
            x: bodyA.velocity.x - bodyB.velocity.x,
            y: bodyA.velocity.y - bodyB.velocity.y
        };

        const impactSpeed = Math.sqrt(
            relativeVelocity.x * relativeVelocity.x + 
            relativeVelocity.y * relativeVelocity.y
        );

        // Approximate impact force based on relative speed and reduced mass
        const reducedMass = (bodyA.mass * bodyB.mass) / (bodyA.mass + bodyB.mass);
        const estimatedForce = impactSpeed * (reducedMass || 1);

        // Check if the collision involves welded bodies
        checkAndBreakWeld(weld, bodyA, bodyB, estimatedForce);
    }
});

Snapping and Removing the Constraint

When the estimated collision force exceeds your defined breakForce, remove the constraint from the active world composite.

function checkAndBreakWeld(constraint, collidingA, collidingB, force) {
    if (constraint.isBroken) return;

    // Verify if either colliding body belongs to the constraint
    const involvesWeld = 
        constraint.bodyA === collidingA || constraint.bodyA === collidingB ||
        constraint.bodyB === collidingA || constraint.bodyB === collidingB;

    if (involvesWeld && force > constraint.breakForce) {
        // Break the weld
        Matter.Composite.remove(engine.world, constraint);
        constraint.isBroken = true;
    }
}

Managing Multiple Breakable Welds

When scaling to structures with multiple breakable parts, maintain an array of active welds and iterate through them during collision processing:

  1. Tag constraints: Attach an identifier or store them in a dedicated list (e.g., breakableConstraints).
  2. Batch removal: Collect all constraints that exceed their breaking thresholds during the collision loop.
  3. World update: Remove broken constraints using Composite.remove(world, constraint) immediately to ensure subsequent physics solver iterations treat the bodies as disconnected.