This article is about coding orbital mechanics in JavaScript and learning the equations needed for creating a demo. This article assumes you already have some experience with coding in JavaScript.

Listen, I like getting right to writing code as much as the next person, but there are a few things we need to learn before that. We will learn about:

  • The equation for distance between two points
  • calculating the amount of force acting upon an object
  • using Newton's gravity equation, and computing the direction of a vector.

The equations

Formula for distance between two points

Computing the distance between two points is easy. Here is the formula for doing so:

d=(x1x0)2+(y1y0)2d = \sqrt{(x_1 - x_0)^2 + (y_1 - y_0)^2}

Here, dd is the distance between the two points, while (x0,y0)(x_0, y_0) and (x1,y1)(x_1, y_1) are the coordinates.

Newton's law of gravity

We use this equation to calculate the force acting upon an object in space, such as a planet or a moon. In other words, this is the force of gravity between two objects.

This is probably the most complicated part of this article, but once you understand it everything else will be simple in comparison. In this equation, FF is equal to the amount of force in newtons NN acting upon an object.

GG is the gravitational constant which is equal to 9.81. MM and mm is the mass of the two objects we are calculating, and dd is the distance between the two objects.

F=GmMd2F = \frac{G \cdot m \cdot M}{d^2}

The direction of a vector

To compute the direction of a vector, we use the atan2 function. This function takes two arguments, the y-coordinate and the x-coordinate, and returns the angle in radians between the positive x-axis and the point given by the coordinates.

o=atan2(x1x0,y1y0)\begin{aligned} o &= \operatorname{atan2}(x_1 - x_0,\, y_1 - y_0) \end{aligned}

In order to extract the x and y components of a force vector, we can use the following equations:

Fx=cos(o)FFy=sin(o)F\begin{aligned} F_x &= \cos(o) \cdot F \\ F_y &= \sin(o) \cdot F \end{aligned}

Astronomical units

Astronomical units are a way of measuring distances in space and is what works best for large distances like between planets and moons. An astronomical unit is the distance between the center of the sun to the center of the earth and is defined as AU = 149.6e9, or about 150 million kilometers.

A demo

This is not to scale.

This is the code

const width = 400;
const height = 400;
const G = 6.67428e-11;
const AU = (149.6e6 * 1000)
var scalevalue = 50;
var speedvalue = 5;
var SCALE = scalevalue / AU
var timestep = 24*3600 / speedvalue

const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");

function gameCenter() {
    return {x: 0.5 * width, y: 0.5 * height};
}

class Scene {
    constructor() {
        this.objects = [];
    }
    
    addObject(obj) {
        this.objects.push(obj);
    }
    
    attraction(self, other) {
        // compute distance between bodies
        const dx = other.x - self.x;
        const dy = other.y - self.y;
        const distance = Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2));

        // compute force
        const F = G * self.mass * other.mass / Math.pow(distance, 2);

        // compute direction
        const o = Math.atan2(dy, dx);
        const Fx = Math.cos(o) * F;
        const Fy = Math.sin(o) * F;

        //console.log(sx, sy, ox, oy, dx, dy, d, F, o, Fx, Fy);
        return [Fx, Fy]
    }
    
    render() {
        ctx.fillStyle = "#000";
        ctx.fillRect(0, 0, width, height);
        
        const changes = [];
        
        this.objects.forEach(o => {
            let total = [0,0];
            
            this.objects.forEach(o2 => {
                if (o !== o2) {
                    const attr = this.attraction(o, o2);
                    total[0] += attr[0];
                    total[1] += attr[1];
                }
            });
            
            changes.push(total);
        });
        
        changes.forEach((change, index) => 
            this.objects[index].updateV(change[0], change[1]));
        this.objects.forEach(o => o.draw(this.context));
    }
}

class Particle {
    constructor(opts) {
        this.x = opts.x;
        this.y = opts.y;
        this.vx = opts.vx;
        this.vy = opts.vy;
        this.radius = opts.radius;
        this.mass = opts.mass;
        this.linewidth = opts.linewidth;
        this.strokestyle = opts.strokestyle;
    }

    update() {
        
    }
    
    updateV(vx, vy) {
        this.vx += vx / this.mass * timestep;
        this.vy += vy / this.mass * timestep;
        
        this.updateP();
    }
    
    updateP() {
        this.x += this.vx * timestep;
        this.y += this.vy * timestep;
    }

    translateCoords(x, y) {
        return [0.5 * width + x * SCALE, 0.5 * height + y * SCALE];
    }

    draw() {
        const coords = this.translateCoords(this.x, this.y);
        
        ctx.lineWidth = this.linewidth;
        ctx.fillStyle = this.strokestyle;
        ctx.beginPath();
        ctx.arc(coords[0], coords[1], this.radius, 0, 2 * Math.PI, false);
        ctx.fill();
    }
}

let sun = new Particle({
    name: "Sun",
    mass: 1.989e30,
    vx: 0,
    vy: 0,
    x: 0,
    y: 0,
    radius: 8,
    linewidth: 3,
    strokestyle: 'yellow'
});

let earth = new Particle({
    name: "Earth",
    mass: 5.972e24,
    vx: 0,
    vy: 30290,
    x: AU,
    y: 0,
    radius: 4,
    linewidth: 1,
    strokestyle: '#60a6d4'
});

const scene = new Scene();
scene.addObject(sun);
scene.addObject(earth);

function gameLoop() {
    scene.render();
    requestAnimationFrame(gameLoop);
}

gameLoop();