Gravitational Slingshot Simulation in Matter.js
This article explains how to simulate gravitational slingshot maneuvers past orbiting moons using the 2D physics library Matter.js. By disabling default Cartesian gravity, implementing an inverse-square law through custom force loops, and balancing orbital velocities, you can model realistic orbital mechanics and gravity-assist trajectories directly in the browser.
1. Disable Default World Gravity
Matter.js defaults to a uniform downward gravity vector suitable for platformers, not celestial mechanics. To model space physics, set the world's vertical and horizontal gravity to zero immediately after creating the engine.
const engine = Matter.Engine.create();
engine.world.gravity.x = 0;
engine.world.gravity.y = 0;2. Define the Bodies
To create a moon slingshot, define at least three bodies:
- Central Planet: A massive, static or semi-static body at the center of the system.
- Moon: A moderately massive body placed in a stable circular orbit around the central planet.
- Spacecraft: A low-mass, high-speed projectile whose path will be altered by the moon.
const { Bodies, Composite } = Matter;
// Central Planet
const planet = Bodies.circle(400, 400, 40, {
isStatic: true,
mass: 10000,
label: 'planet'
});
// Orbiting Moon
const moonDistance = 200;
const moon = Bodies.circle(400 + moonDistance, 400, 15, {
mass: 500,
frictionAir: 0,
label: 'moon'
});
// Spacecraft
const ship = Bodies.circle(150, 550, 4, {
mass: 1,
frictionAir: 0,
label: 'ship'
});
Composite.add(engine.world, [planet, moon, ship]);Ensure frictionAir is set to 0 for moving
bodies so that energy is conserved in the vacuum of space.
3. Initialize Stable Orbital Velocity for the Moon
To maintain a circular orbit around the central planet, the moon requires a perpendicular velocity vector where gravitational force equals centripetal force:
\[v = \sqrt{\frac{G \cdot M_{\text{planet}}}{r}}\]
Set this initial velocity manually:
const G = 0.5; // Custom gravitational scaling constant
const initialSpeed = Math.sqrt((G * planet.mass) / moonDistance);
Matter.Body.setVelocity(moon, {
x: 0,
y: -initialSpeed
});4. Implement Inverse-Square Gravity
Matter.js lacks native N-body attraction, so you must apply
gravitational forces before every engine step using
Matter.Events.on(engine, 'beforeUpdate', callback).
To prevent division-by-zero errors and erratic tunneling during close flybys, include a gravitational softening parameter (\(\epsilon\)).
Matter.Events.on(engine, 'beforeUpdate', () => {
const bodies = [planet, moon, ship];
const softening = 100; // Prevents extreme acceleration at close proximities
// Apply gravity between all interacting pairs
applyGravity(planet, moon);
applyGravity(planet, ship);
applyGravity(moon, ship);
function applyGravity(bodyA, bodyB) {
const dx = bodyA.position.x - bodyB.position.x;
const dy = bodyA.position.y - bodyB.position.y;
const distanceSq = dx * dx + dy * dy + softening;
const distance = Math.sqrt(distanceSq);
// Newton's Universal Gravitation: F = G * (m1 * m2) / r^2
const forceMagnitude = (G * bodyA.mass * bodyB.mass) / distanceSq;
const force = {
x: (dx / distance) * forceMagnitude,
y: (dy / distance) * forceMagnitude
};
// Apply equal and opposite forces if bodies are dynamic
if (!bodyB.isStatic) {
Matter.Body.applyForce(bodyB, bodyB.position, force);
}
if (!bodyA.isStatic) {
Matter.Body.applyForce(bodyA, bodyA.position, { x: -force.x, y: -force.y });
}
}
});5. Executing the Slingshot Maneuver
A gravitational slingshot (gravity assist) transfers orbital momentum from the orbiting moon to the spacecraft. For the maneuver to succeed:
- Trajectory Planning: Aim the spacecraft behind the moon relative to the moon's direction of travel.
- Hyperbolic Excess: As the spacecraft enters the moon's gravitational sphere of influence, it accelerates toward the moon.
- Momentum Exchange: While energy is conserved relative to the moon, the spacecraft exits the encounter with added kinetic energy relative to the central planet, gaining up to twice the moon's orbital velocity.
Launch the spacecraft by setting its initial velocity along an intercept vector:
Matter.Body.setVelocity(ship, {
x: 2.5,
y: -3.2
});6. Tuning Simulation Accuracy
Standard Euler integrators struggle with orbital stability over long durations. Use these adjustments in Matter.js to maintain precision:
- Increase Engine Iterations: Increase
engine.positionIterationsandengine.velocityIterations(e.g., from default6to16) to decrease integration error. - Decrease Fixed Timestep: Update the engine using a
smaller, consistent delta time (such as
1000 / 120for 120 Hz updates) instead of a variable frame-rate step to preserve angular momentum.