p5.js Simulations
Overview
Coding visual simulations of orbits to share on the internet could not be easier with p5.js. This JavaScript library is set out in such an intuitive way that really allows you to take full advantage of JavaScript's ability to create visuals and the object-oriented programming to create some amazing simulations. The way you write the code is really useful in terms of stepping through time for a system, and with each step updating the acceleration, velocity and position of every object - making it perfect for simulating gravitational attraction!This is an example of an N-Body problem which I have coded in p5.js. Thanks to the object-oriented approach, you can select any number for of bodies and the program simply adds a Body object to the list which is iterated through.
The N-Body Problem
The Physics Behind It
For this animation, the values used for masses and positions (the only initial conditions set) are all arbitrary, with a circle's radius representing its mass. This is becuase the simulation does not aim to recreate an existing system of planets, but instead to show the interesting, complex, and chaotic results of gravitational attraction between planets.You may also notice that the bodies seems to stay relatively centered; this is because the initial velocity of each body is 0, so the initial momentum in the system is 0, therefore each body moving a given direction will be countered by a movement of planets in the opposite direction to ensure the conservation of momentum. Over time, this ensures that the planets centre of mass remains where it was at the start, somewhere on the canvas.
To add something a little extra, I decided to give the planets colours and make these colours correspond to their speed. Looking at the animation, you may be able to see that a planet appears more blue when it has a lower speed. The amount of green a body has is constant, and the amount of red is decided simply by which index it is in the bodies array.
The Code
For anyone who is interested in actually understanding the code, I would really recommend following the research links on this page, as they explain the code much better than I can, especially if you are interested in following along to make your own simulation. However, if you would like to see the finish product and look at my code, I have annotated it below.
let bodies = []; // initialise array of bodies
let n = 7; // set value of n for the initial simulation
function setup() {
// creates a canvas and then appends it to the div with the id "canvasContainer" so it displays on the page
bodies = []
canvas = createCanvas(windowWidth/2, windowHeight/2);
canvas.parent("canvasContainer");
for (var i = 0; i < n; i++) {
// creates a new body with a mass of 10-50, at a random position within the middle 80% of the canvas
bodies.push(new Body(random(10,50), random(width*0.1, width*0.9), random(height*0.1, height*0.9), i));
}
}
function draw() {
// draw a rectangle to clear the background, but with opacity to create a trail effect
fill(0, 20);
rect(0, 0, width, height);
// calculate the force of attraction between each pair of bodies
for (var i = 0; i < bodies.length; i++) {
for (var j = 0; j < bodies.length; j++) {
if (i != j) {
let force = bodies[j].attract(bodies[i]);
bodies[i].applyForce(force);
}
}
}
// update and show each body
for (var i = 0; i < bodies.length; i++) {
bodies[i].update();
bodies[i].show();
}
}
class Body {
constructor(mass, x, y, index) {
this.index = index;
this.mass = mass;
this.position = createVector(x, y);
this.velocity = createVector(0, 0);
this.acceleration = createVector(0, 0);
}
// returns the force of attraction between this body and the given body
attract(planet) {
let force = p5.Vector.sub(this.position, planet.position);
let distance = force.mag();
distance = constrain(distance, 5, 25);
let strength = (0.4 * this.mass * planet.mass) / (distance * distance);
force.setMag(strength);
return force;
}
// updates the body's position based on its velocity and acceleration
update() {
this.velocity.add(this.acceleration);
this.position.add(this.velocity);
this.acceleration.mult(0);
}
// applies a force to the body, e.g. gravity
applyForce(force) {
let f = force.copy();
f.div(this.mass);
this.acceleration.add(f);
}
// draws the body on the canvas, with corresponding fill colour for speed and index
show() {
fill(255*this.index/n,125,255/this.velocity.mag());
circle(this.position.x, this.position.y, this.mass);
}
}
setup()
draw()
Research links
- The Nature of CodeThe Nature of Code Book natureofcode.com