How to Weld Two Bodies Dynamically in Matter.js

This article explains how to dynamically weld two physics bodies together at runtime in Matter.js without reconstructing them into a compound body. By using multiple rigid constraints with spatial offsets, you can lock the relative translation and rotation between existing bodies on the fly, avoiding the performance penalties, body recreation overhead, and reference invalidation associated with rebuilding compound bodies.

Why Avoid Compound Bodies at Runtime

Creating compound bodies in Matter.js using Body.create({ parts: [...] }) requires defining all parts during initialization. Merging existing dynamic bodies into a compound body during runtime forces you to destroy the original bodies, calculate new centers of mass, and recreate new references. This resets velocities, collision listeners, and custom state variables attached to those bodies.

Using constraints allows both bodies to maintain their independent identities, properties, and collision filters while moving as a single rigid unit.

The Dual-Constraint Technique

A single Matter.Constraint acts as a pivot joint, locking linear distance between two points while allowing free angular rotation. To completely weld two bodies and prevent both linear movement and rotation, you must create at least two parallel constraints with offset anchor points.

Implementation

To weld Body A and Body B at their current relative positions, calculate local offset anchors and connect them using two constraints with a stiffness of 1 (or close to 1 depending on engine iterations).

const { Constraint, Composite, Vector } = Matter;

function weldBodies(world, bodyA, bodyB) {
  // Define an offset distance for the dual anchor points
  const offset = 20;

  // Calculate local anchor positions on Body A
  const pointA1 = { x: -offset, y: 0 };
  const pointA2 = { x: offset, y: 0 };

  // Convert Body A local points to world coordinates
  const worldPoint1 = Vector.add(bodyA.position, Vector.rotate(pointA1, bodyA.angle));
  const worldPoint2 = Vector.add(bodyA.position, Vector.rotate(pointA2, bodyA.angle));

  // Convert world coordinates to Body B's local coordinate space
  const pointB1 = Vector.rotate(Vector.sub(worldPoint1, bodyB.position), -bodyB.angle);
  const pointB2 = Vector.rotate(Vector.sub(worldPoint2, bodyB.position), -bodyB.angle);

  // Create two rigid constraints connecting the calculated anchor pairs
  const constraint1 = Constraint.create({
    bodyA: bodyA,
    pointA: pointA1,
    bodyB: bodyB,
    pointB: pointB1,
    length: 0,
    stiffness: 1
  });

  const constraint2 = Constraint.create({
    bodyA: bodyA,
    pointA: pointA2,
    bodyB: bodyB,
    pointB: pointB2,
    length: 0,
    stiffness: 1
  });

  // Add the constraints to the physics world
  Composite.add(world, [constraint1, constraint2]);

  return [constraint1, constraint2];
}

Breaking or Releasing the Weld

To separate welded bodies, remove the constraints from the world composite:

function unweldBodies(world, constraints) {
  Composite.remove(world, constraints);
}

Once the constraints are removed, both bodies instantly resume independent physical behavior, maintaining any momentum transferred during their welded state.

Ensuring Weld Rigidity

Matter.js resolves constraints iteratively. If the weld exhibits minor bending or elasticity under heavy forces:

  1. Increase Constraint Iterations: Raise engine.constraintIterations (default is 2) to 4 or 8 in your engine configuration to reduce joint compliance.
  2. Increase Anchor Separation: Wider spacing between pointA1 and pointA2 provides greater leverage against rotational forces, significantly reducing visible flexing.