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:

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:

  1. Trajectory Planning: Aim the spacecraft behind the moon relative to the moon's direction of travel.
  2. Hyperbolic Excess: As the spacecraft enters the moon's gravitational sphere of influence, it accelerates toward the moon.
  3. 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: