Matter.js Extreme Mass Ratio Collision Instability
When simulating physics in Matter.js, pairing bodies with drastically disparate masses—such as a mass of 1 interacting with a mass of 10,000—frequently triggers severe physics artifacts, including jittering, tunneling, and explosive velocity spikes. This article breaks down the technical reasons why extreme mass ratios break the stability of the Matter.js iterative constraint solver, covering impulse distribution, position correction overshoot, and floating-point limitations, along with concrete methods to prevent these issues.
The Sequential Impulse Solver
Matter.js relies on an iterative sequential impulse solver to resolve collisions and constraints. When two bodies intersect, the engine calculates an impulse along the contact normal to prevent penetration and simulate restitution and friction.
The impulse magnitude \(J\) is inversely proportional to the sum of the inverse masses of the colliding bodies:
\[\text{Effective Mass} = \frac{1}{\frac{1}{m_A} + \frac{1}{m_B}}\]
When the mass of body \(B\) (\(m_B\)) is orders of magnitude larger than body \(A\) (\(m_A\)), \(1/m_B\) approaches zero. As a result, the effective mass is dictated almost entirely by the lighter body (\(m_A\)). While mathematically this represents an immovable object relative to the light body, the engine's discrete-time numerical integration turns this theoretical model into an unstable feedback loop.
Key Causes of Instability
1. Velocity Overshoot and Energy Injection
Because dynamic bodies are resolved incrementally across frames, an extremely heavy dynamic object barely slows down during an impact. In contrast, the lighter body receives nearly the entirety of the calculated velocity change. If the heavier body penetrates even slightly into the lighter body during a single time step, the impulse generated to halt penetration can accelerate the lighter object to an extreme speed in a single frame. This sudden injection of kinetic energy makes the simulation appear explosive.
2. Failure of Iterative Convergence
Matter.js does not solve all collision constraints simultaneously with a global matrix; it iterates through contact pairs sequentially. By default, the engine uses:
engine.positionIterations = 6engine.velocityIterations = 4
When mass ratios are moderate (e.g., 1:1 to 1:10), these iterations are sufficient to settle the contact forces smoothly. With extreme mass ratios, the solver cannot reach equilibrium within 4 to 6 passes. The lighter body bounces violently between the heavy body and any adjacent surfaces (such as the floor) within a single frame, resulting in rapid micro-collisions that destabilize the solver state.
3. Position Correction (Baumgarte Stabilization) Overshoot
Matter.js uses position correction to push overlapping bodies apart and counteract numerical drift. When two bodies overlap, the lighter body is forced to absorb almost 100% of the positional shift. Because the overlap resolution is applied abruptly to avoid sinking, the lighter body is displaced drastically over one step, frequently teleporting it inside neighboring geometry or far away from the contact point.
4. Floating-Point Precision Loss
Calculations involving large disparities in magnitude lead to rounding errors. Adding tiny inverse mass values to comparatively large numbers reduces the precision of normal and tangent vectors. In tight stacking scenarios or high-speed collisions, these precision deficits cause erratic normal calculations, manifesting as random lateral slides or perpetual jittering.
Solutions to Stabilize Collisions
- Clamp Mass Ratios: Keep dynamic mass ratios within
a 1:10 to 1:50 range. If an object is meant to represent something
massive like a planet or a building, avoid giving it an arbitrary
dynamic mass like
1,000,000; setisStatic: trueinstead. - Increase Solver Iterations: Double or triple the
iteration counts in the engine configuration to give the solver more
passes to reach convergence:
engine.positionIterations = 12; engine.velocityIterations = 8; - Use Sub-stepping: Reduce the simulation time step by updating the engine multiple times per render frame (e.g., running two 8.33ms updates instead of one 16.66ms update). Smaller time intervals minimize penetration depth before resolution occurs.
- Tune Restitution and Slop: Set
restitution: 0on heavy-to-light contact pairs to prevent runaway elastic bouncing, and adjustbody.slopto allow a tiny, stable threshold of geometric penetration before aggressive position corrections trigger.