Modeling Centripetal Acceleration in Matter.js
This article details how to accurately model centripetal acceleration for rotating bodies connected to central tethers in the Matter.js 2D physics engine. It explores using rigid distance constraints to generate natural tension, setting correct initial tangential velocities, mitigating constraint drift through solver configuration, and applying manual force vectors for precise circular motion simulations.
The Physics of Rotation in Matter.js
In classical mechanics, a body of mass \(m\) traveling at a tangential velocity \(v\) around a fixed point at a radius \(r\) requires an inward centripetal force:
\[F_c = \frac{m v^2}{r} = m \omega^2 r\]
Matter.js uses an iterative constraint solver rather than an analytical orbital engine. When you link a body to a fixed point using a constraint, the engine approximates this inward force by resolving constraint tensions each step.
Setting Up a Rigid Tether with Constraints
The most straightforward method to model a tethered rotating body is
using Matter.Constraint.
const { Engine, Render, Runner, Bodies, Composite, Constraint, Body, Vector } = Matter;
const engine = Engine.create();
const world = engine.world;
// Disable gravity for purely horizontal orbital mechanics
engine.gravity.y = 0;
// Central anchor (static)
const center = { x: 400, y: 300 };
const radius = 150;
// Orbiting body
const bob = Bodies.circle(center.x + radius, center.y, 20, {
mass: 2,
frictionAir: 0 // Eliminate air resistance to maintain constant speed
});
// Central tether constraint
const tether = Constraint.create({
pointA: center,
bodyB: bob,
pointB: { x: 0, y: 0 },
length: radius,
stiffness: 1, // Maximum rigidity
damping: 0
});
Composite.add(world, [bob, tether]);Applying Tangential Velocity
To initiate circular motion, you must provide an initial velocity vector perpendicular to the tether axis. If the body begins directly to the right of the anchor at \((x + r, y)\), the initial velocity must point entirely along the positive or negative Y-axis.
const tangentialSpeed = 5; // Pixels per tick
// Set perpendicular velocity
Body.setVelocity(bob, { x: 0, y: tangentialSpeed });Managing Constraint Stretching and Numerical Drift
Because Matter.js relies on sequential impulse solving, a single constraint can experience elasticity (drift) at high velocities, causing the orbital radius to stretch and distort the centripetal acceleration.
To fix this:
- Increase Position and Velocity Iterations:
Increasing engine solver iterations makes the constraint effectively
stiffer.
engine.positionIterations = 12; engine.velocityIterations = 8; - Sub-stepping the Engine: Running smaller, more frequent delta time updates reduces the displacement error per frame.
Manual Centripetal Force Vectoring
If you require mathematically perfect orbits without constraint
elasticity or stiff spring artifacts, calculate and apply the inward
acceleration vector explicitly in a beforeUpdate event.
Matter.Events.on(engine, 'beforeUpdate', () => {
const toCenter = Vector.sub(center, bob.position);
const distance = Vector.magnitude(toCenter);
if (distance === 0) return;
const normal = Vector.normalise(toCenter);
const currentSpeed = Vector.magnitude(bob.velocity);
// Calculate required centripetal force magnitude: (m * v^2) / r
const forceMagnitude = (bob.mass * Math.pow(currentSpeed, 2)) / distance;
const centripetalForce = Vector.mult(normal, forceMagnitude);
// Apply the force toward the center of rotation
Body.applyForce(bob, bob.position, centripetalForce);
});Using manual force vectoring eliminates the need for
Matter.Constraint entirely, ensuring stable circular
trajectories without numerical instability, tether sagging, or
artificial energy inflation.