Simulating Spur Gears in Matter.js Without Jamming

Simulating physical spur gears in 2D physics engines presents a classic challenge: discrete collision detection frequently causes interpenetrating gear teeth, leading to violent instability, friction lockups, or tooth jamming. This guide details how to bypass these physical contact pitfalls in Matter.js by utilizing mathematical constraint coupling for realistic torque transfer, as well as how to configure physical tooth geometries if rigid-body collisions are strictly required.

The Core Problem: Why Physical Teeth Jam

Matter.js relies on an iterative impulse solver. Under torque, rigid-body gear teeth press against each other at sharp angles. If rotational velocity is high or the simulation step size is too large, the corner of one tooth penetrates deeply past the boundary of the opposing tooth. On the next engine tick, the collision solver attempts to eject the overlapping shapes instantly, causing high-energy rebound ("explosions") or locking the bodies together in an unresolvable collision state.

The most robust and performant way to transfer torque between gears in Matter.js is to decouple the visual gear teeth from the physics calculation, linking their angular velocities programmatically.

  1. Create Circular Physics Bodies: Represent the gears as simple circular bodies with no physical teeth, pinned at their centers using Matter.Constraint.create() with a stiffness of 1.
  2. Apply Visual Teeth: Render teeth as visual sprites or non-colliding compound bodies (using collisionFilter: { mask: 0 }).
  3. Link Rotation via Engine Events: Calculate the gear ratio based on the pitch radii of the two gears (\(Ratio = \frac{Radius_A}{Radius_B}\)) and sync their angular motion before each engine update.
const gearRatio = radiusA / radiusB;

Matter.Events.on(engine, 'beforeUpdate', () => {
    // If torque is applied to gear A, drive gear B in the opposite direction
    const targetAngularVelocity = -gearA.angularVelocity * gearRatio;
    Matter.Body.setAngularVelocity(gearB, targetAngularVelocity);
});

To allow bidirectional torque transfer where an external force applied to either gear affects the other, dynamically balance their angular velocities based on their relative moments of inertia:

Matter.Events.on(engine, 'beforeUpdate', () => {
    const relativeSpeed = gearA.angularVelocity * gearRatio + gearB.angularVelocity;
    const correction = relativeSpeed * 0.5;

    Matter.Body.setAngularVelocity(gearA, gearA.angularVelocity - correction / gearRatio);
    Matter.Body.setAngularVelocity(gearB, gearB.angularVelocity - correction);
});

Method 2: Physical Mesh Collision Without Jamming

If physical tooth-on-tooth interaction is mandatory, specific physics properties and geometries must be tuned to minimize contact errors.