Orbital Simulation

In p5.js

Made by a human

This project presents a simple N-body simulation built using p5.js, where multiple objects move and interact through a gravitational-like force in a two-dimensional space.
A main body simulating the sun is placed in the center of the scene and acts as a central moving object.
setup(){ MainBody = new ObjectWithG(width/2, height/2, 8, 0, 0, color(0, 0, 0)); }
Around it (still in the setup function), many smaller bodies are randomly distributed, each with its own position and initial velocity.
for (var i = 0; i < NumberOFBodies; i++){ bodies.push(new ObjectWithG(random(50, width-50), random(50, height-50), 4, random(-2, 2), random(-2, 2))); }
During the simulation, all bodies are updated continuously.
for(var i = 0; i < NumberOFBodies; i++){ bodies[i].getForce(MainBody, G); bodies[i].update(); bodies[i].show(); }
The smaller bodies are attracted toward the main body through a gravitational force calculated in a method of the class ObjectWithG.
getForce(other, G){ //other is another object let dir = p5.Vector.sub(other.Vect, this.Vect); //direction of the Vector let distance = dir.mag(); //magnitude = size of the Vector ----->.mag() > -->.mag() distance = constrain(distance, 1, 15); //so that if an object comes too close to the main Object its speed doesn't get too big let forceMag = G * ((this.mass * other.mass) / (distance * distance)); //Newton's formula dir.normalize(); dir.mult(forceMag); let acc = p5.Vector.div(dir, this.mass); this.acc.add(acc); }
That force depends on their mass and distance according to Newton's universal law of gravitation:
Formula
To improve numerical stability and smoothness, the simulation is calculated in multiple substeps per frame:
let substeps = 30 for (let iter = 0; iter < substeps; iter++){ //repeats this code 30 times per frame }

Class Explanation

Constructor method

The radius of the body is defined during initialization. this.Vect is the vector that represents the position of the body, and its x and y coordinates are also set during initialization. The mass is calculated by dividing the body's radius by ten. this.acc is a vector that is added to this.vel, which is then added to this.Vect, therefore changing the body's position.
constructor(x, y, r, vx=0, vy=0, c = undefined){ if (c !== undefined){ this.c = color(0, 0, 0) } else{ this.c = color(100, 255, 100) } this.radius = r; this.Vect = createVector(x, y); this.mass = this.radius/10; this.acc = createVector(0, 0); this.vel = createVector(vx, vy); }

Get Force method

To calculate the force that a body is experiencing we use Vectors:
let dir = p5.Vector.sub(other.Vect, this.Vect); let acc = p5.Vector.div(dir, this.mass);
When we normalize the direction vector we transform the vector's length (magnitude) to 1 so we turn it into the force Vector:
dir.normalize(); //mag = 1 dir.mult(forceMag); //mag = 1*forceMag
The last step to calculate the force is to turn the force into acceleration:
let acc = p5.Vector.div(dir, this.mass); // a = F/Mass Newton's second law this.acc.add(acc);
Full method:
getForce(other, G){ let dir = p5.Vector.sub(other.Vect, this.Vect); let distance = dir.mag(); distance = constrain(distance, 15, 30); let forceMag = G * ((this.mass * other.mass) / (distance * distance)); //Newton's formula dir.normalize(); dir.mult(forceMag); let acc = p5.Vector.div(dir, this.mass); //a = F/Mass this.acc.add(acc); }

Update and Show methods

The update method applies Forces in this order: Force → acceleration → velocity → Position
update(){ this.vel.add(this.acc) this.Vect.add(this.vel) this.acc.mult(0) }
The show method simply draws a circle with a blue outline
show(){ stroke(0, 0, 200) strokeWeight(1) fill(this.c) circle(this.Vect.x, this.Vect.y, this.radius*2) }

Detect Collisions

The final method of this class, detectCollision, checks if the distance between two bodies is smaller than the sum of their radius by creating a vector between the two, checking the vector's magnitude and returning true if the distance is smaller than the sum of their radius.
detectCollision(other){ if(p5.Vector.sub(other.Vect, this.Vect).mag() < this.radius + other.radius){ return true } return false }

Full Class

class ObjectWithG{ constructor(x, y, r, vx=0, vy=0, c = undefined){ if (c !== undefined){ this.c = color(0, 0, 0) } else{ this.c = color(100, 255, 100) } this.radius = r; this.Vect = createVector(x, y); this.mass = this.radius/10; this.acc = createVector(0, 0); this.vel = createVector(vx, vy); } getForce(other, G){ let dir = p5.Vector.sub(other.Vect, this.Vect); let distance = dir.mag(); distance = constrain(distance, 15, 30); let forceMag = G * ((this.mass * other.mass) / (distance * distance)); dir.normalize(); dir.mult(forceMag); let acc = p5.Vector.div(dir, this.mass); this.acc.add(acc); } update(){ this.vel.add(this.acc) this.Vect.add(this.vel) this.acc.mult(0) } show(){ stroke(0, 0, 200) strokeWeight(1) fill(this.c) circle(this.Vect.x, this.Vect.y, this.radius*2) } detectCollision(other){ if(p5.Vector.sub(other.Vect, this.Vect).mag() < this.radius + other.radius){ return true } return false } }

Full Code

function setup() { createCanvas(min(windowWidth, windowHeight)-3, min(windowWidth, windowHeight)-3); MainBody = new ObjectWithG(width/2, height/2, 8, 0, 0, color(0, 0, 0)); substeps = 3 G = 2/substeps NumberOFBodies = 5 print(NumberOFBodies) bodies = [] for (var i = 0; i < NumberOFBodies; i++){ bodies.push(new ObjectWithG(random(50, width), random(50, height-50), 6, random(-1, 1), random(-1, 1))) } } function draw() { for (let iter = 0; iter < substeps; iter++){ background(255, 10) MainBody.show(color(255, 0, 0)); for(var i = 0; i < NumberOFBodies; i++){ if (bodies[i] !== undefined){ bodies[i].getForce(MainBody, G) bodies[i].update() bodies[i].show() if(bodies[i].detectCollision(MainBody)){ background(0, 255, 255) bodies[i] = undefined } } } } }
5

By tracing out the body's position you can get cool patterns

img1 img2

References:

Newton's Law of Gravitation (Wikipedia) Gravitational Attraction (The Coding Train) NASA: Basics of Space Flight 3Blue1Brown: Vectors Explained 3Blue1Brown Video: Vectors