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;