Why Matter.js Stacked Boxes Jitter and How to Fix It
Simulating stacked boxes in Matter.js frequently leads to jitter, sinking, or sudden collapse because the 2D physics engine uses iterative impulse resolution to resolve multiple contacts simultaneously. In a vertical stack, normal forces accumulate down the column, magnifying minor numerical inaccuracies with every frame. This guide covers the primary technical causes behind stacking instability in Matter.js—including solver iterations, non-zero restitution, unstable time steps, and disabled body sleeping—along with the specific configuration changes required to achieve a stable, motionless stack.
Insufficient Engine Iterations
Matter.js resolves collisions using an iterative constraint solver. In a stack, the bottom box must support the weight of every box above it, requiring forces to propagate correctly through multiple contact points in a single frame.
By default, Matter.js uses relatively low iteration counts:
engine.positionIterationsdefaults to 6.engine.velocityIterationsdefaults to 4.
When these values are too low, the engine resolves overlapping boundaries incompletely, leading to accumulated penetration errors that present as visible vibration or wobbling. Increasing both values forces the engine to calculate contacts more thoroughly:
engine.positionIterations = 20;
engine.velocityIterations = 16;Non-Zero Restitution (Bounciness)
Even small restitution values on stacked bodies create
continuous micro-bounces. When multiple bodies transfer energy downward,
restitution prevents the stack from settling to a complete rest.
Ensure that all bodies intended for stacking have their restitution explicitly set to zero:
const box = Bodies.rectangle(x, y, width, height, {
restitution: 0,
friction: 0.8
});Disabled Body Sleeping
Without sleeping enabled, the physics engine continuously computes collision impulses for resting objects, no matter how small the movement. Because floating-point numbers fluctuate slightly on every frame, boxes never truly freeze.
Enabling body sleeping instructs Matter.js to stop simulating bodies once their motion falls below a defined velocity threshold:
engine.enableSleeping = true;When a box comes to rest, it transitions into a sleeping state and ceases to jitter until another moving body impacts it.
Inconsistent Time Steps
Using a variable delta time inside your render loop (such as
requestAnimationFrame delta times) destabilizes numerical
integration. If the frame rate drops or spikes, the physics calculation
takes uneven integration steps, causing bodies to penetrate too deeply
and violently repel outward on the subsequent frame.
Always run the Matter.js engine with a fixed time step via
Matter.Runner or a custom loop:
const runner = Runner.create({
isFixed: true,
delta: 1000 / 60
});
Runner.run(runner, engine);Improper Slop Settings
The slop property dictates the allowed overlap between
bodies before positional correction forces are applied.
- If
slopis too high, boxes visibly sink into one another before bouncing back up. - If
slopis too low (or zero), the solver over-corrects micro-penetrations, causing violent vibration.
The default body slop is 0.05. For precise
stacking, slightly lowering slop to 0.01—in
combination with increased position iterations—often smooths out jitter
without causing instability.
Mass Ratios and Scale
Matter.js calculates impulses using mass ratios. If the stack contains bodies with drastically different masses, or if the bodies are extremely small or large compared to default units, the solver struggles to distribute forces evenly. Keep masses relatively uniform throughout the stack and model bodies near normal canvas pixel dimensions (e.g., 30–100 pixels) to avoid extreme numerical scales.