Newton's Gravity Between Two Bodies in Matter.js
This article explains how to simulate mutual gravitational attraction between two bodies in Matter.js using Newton's law of universal gravitation. You will learn how to disable standard engine gravity, calculate the distance and directional vectors between bodies, compute the gravitational force magnitude, and apply equal and opposite forces on each simulation step.
The Gravitational Formula
Newton's law of universal gravitation states that the attractive force (\(F\)) between two masses (\(m_1\) and \(m_2\)) is proportional to their masses and inversely proportional to the square of the distance (\(r\)) between their centers:
\[F = G \frac{m_1 m_2}{r^2}\]
In a physics engine:
- \(G\) is an arbitrary gravitational constant adjusted to match the visual scale and speed desired in your simulation.
- \(m_1\) and \(m_2\) are derived from
body.mass. - \(r\) is the Euclidean distance
between
bodyA.positionandbodyB.position.
1. Disable Engine Gravity
Matter.js applies a default downward gravity across the entire world. To simulate orbital or mutual celestial mechanics, set the global gravity to zero:
engine.gravity.scale = 0;2. Calculate and Apply the Force
Mutual attraction requires updating the applied forces before every
engine step. Use the beforeUpdate event provided by
Matter.Events to calculate and apply forces
iteratively.
const { Engine, Render, Runner, Bodies, Composite, Body, Events, Vector } = Matter;
// Initialize engine and world
const engine = Engine.create();
engine.gravity.scale = 0;
// Create two bodies
const bodyA = Bodies.circle(300, 300, 30, { mass: 1000 });
const bodyB = Bodies.circle(500, 300, 15, { mass: 10 });
Composite.add(engine.world, [bodyA, bodyB]);
// Gravitational constant tuned for screen coordinates
const G = 0.001;
// Softening factor to prevent infinite acceleration at close range
const minDistance = 10;
Events.on(engine, 'beforeUpdate', () => {
// Determine the displacement vector from bodyA to bodyB
const dx = bodyB.position.x - bodyA.position.x;
const dy = bodyB.position.y - bodyA.position.y;
// Calculate distance
const distanceSquared = dx * dx + dy * dy;
const distance = Math.max(Math.sqrt(distanceSquared), minDistance);
// Calculate Newton's gravitational force magnitude
const forceMagnitude = (G * bodyA.mass * bodyB.mass) / (distance * distance);
// Normalize displacement to produce a unit vector, then multiply by magnitude
const forceVector = {
x: (dx / distance) * forceMagnitude,
y: (dy / distance) * forceMagnitude
};
// Apply attraction to bodyA toward bodyB
Body.applyForce(bodyA, bodyA.position, forceVector);
// Apply equal and opposite reaction to bodyB toward bodyA
Body.applyForce(bodyB, bodyB.position, {
x: -forceVector.x,
y: -forceVector.y
});
});Considerations for Stability
- Softening Factor: When two bodies get extremely
close (\(r \to 0\)), calculated forces
approach infinity, causing bodies to fling out of the canvas. Clamping
the distance with a
minDistancethreshold prevents this instability. - Initial Velocity: To achieve a stable orbit rather
than a direct collision, assign an initial tangential velocity to the
lighter body using
Body.setVelocity(bodyB, { x: 0, y: 3 }).