Handling Cargo Center of Mass Shifts in Matter.js

Simulating shifting cargo inside a vehicle or container requires managing how sudden shifts in the center of mass (COM) affect stability, trajectory, and rotation. In Matter.js, this problem is typically solved either by modeling cargo as autonomous bodies colliding inside a hollow container or by dynamically recalculating and dampening the center of mass on a single compound body. This guide explains how to implement both techniques while mitigating physical instability, unexpected rotational acceleration, and velocity spikes.

Approach 1: Physical Internal Cargo (Hollow Container)

The most physically accurate method lets Matter.js handle the mass distribution naturally by building a hollow container out of composite walls and placing free-moving cargo bodies inside.

1. Build the Container

Create a hollow vessel using multiple static or dynamic rectangle bodies held together using Body.create({ parts: [...] }):

const wallThickness = 10;
const width = 200;
const height = 100;

const bottom = Bodies.rectangle(x, y + height / 2, width, wallThickness);
const left = Bodies.rectangle(x - width / 2, y, wallThickness, height);
const right = Bodies.rectangle(x + width / 2, y, wallThickness, height);
const top = Bodies.rectangle(x, y - height / 2, width, wallThickness);

const container = Body.create({
  parts: [bottom, left, right, top],
  friction: 0.8
});

2. Add Shifting Cargo Inside

Add smaller bodies inside the cavity. Give them mass, friction, and restitution:

const cargo = Bodies.rectangle(x, y, 40, 40, {
  mass: 10,
  friction: 0.4,
  restitution: 0.1
});

Composite.add(world, [container, cargo]);

Because the cargo moves independently, the total center of mass of the system shifts automatically as forces act on the container.


Approach 2: Abstracted Center of Mass Updates via Body.setCentre

If modeling individual internal items is too computationally expensive or causes clipping issues, you can simulate cargo shifts programmatically by manipulating the container's center of mass using Matter.Body.setCentre.

1. The Offset Problem

Calling Body.setCentre(body, offset, true) changes the anchor around which the body rotates. However, applying a sudden shift instantly teleports the geometric origin and causes an abrupt surge in angular velocity due to the conservation of angular momentum.

2. Smooth Transitions Using Interpolation

Never snap the center of mass directly to a new point during a cargo shift. Instead, step it toward the target offset gradually each engine tick:

// Current offset state
let currentOffset = { x: 0, y: 0 };
let targetOffset = { x: 50, y: 0 }; // Cargo shifted to the right

Events.on(engine, 'beforeUpdate', () => {
  // Linear interpolation (lerp) factor
  const lerpFactor = 0.05;

  const dx = (targetOffset.x - currentOffset.x) * lerpFactor;
  const dy = (targetOffset.y - currentOffset.y) * lerpFactor;

  if (Math.abs(dx) > 0.001 || Math.abs(dy) > 0.001) {
    currentOffset.x += dx;
    currentOffset.y += dy;

    // Apply the incremental center shift relative to the body
    Body.setCentre(containerBody, { x: dx, y: dy }, true);
  }
});

Mitigating Instabilities and Spikes

Sudden center of mass changes can introduce non-physical forces that flip or vibrate the body. Apply the following corrections:

Clamp Angular Velocity

When mass shifts, clamp body.angularVelocity to prevent high-speed flipping:

const MAX_ANGULAR_VELOCITY = 0.15;

Events.on(engine, 'afterUpdate', () => {
  if (Math.abs(containerBody.angularVelocity) > MAX_ANGULAR_VELOCITY) {
    Body.setAngularVelocity(
      containerBody,
      Math.sign(containerBody.angularVelocity) * MAX_ANGULAR_VELOCITY
    );
  }
});

Adjust Moment of Inertia

A body whose mass moves outward gains rotational resistance. Update the body's inertia proportionally to the distance of the cargo from the center:

// Base inertia plus additional rotational resistance: I = I_base + m * d^2
const cargoMass = 10;
const distanceSq = currentOffset.x ** 2 + currentOffset.y ** 2;
const adjustedInertia = baseInertia + cargoMass * distanceSq;

Body.setInertia(containerBody, adjustedInertia);

Increase Solver Iterations

If loose cargo inside a container tunnels through walls during high-speed shifts, increase solver precision on the Engine:

engine.positionIterations = 10;
engine.velocityIterations = 8;