var WIDTH = window.innerWidth, HEIGHT = window.innerHeight, PARTICLE_COUNT = 20,
static fromPolar(radius, angle) {
return new Vector2D(radius * Math.cos(angle), radius * Math.sin(angle));
return a.x*b.x + a.y*b.y;
return Math.sqrt(this.x*this.x + this.y*this.y);
return this.x*this.x + this.y*this.y;
return Math.atan2(this.y, this.x);
return new Vector2D(this.x + vector.x, this.y + vector.y);
return new Vector2D(this.x * scalar, this.y * scalar);
var x = this.x, y = this.y;
this.x = x * Math.cos(angle) - x * Math.sin(angle);
this.y = y * Math.sin(angle) + y * Math.cos(angle);
this.x * Math.cos(angle) - this.x * Math.sin(angle),
this.y * Math.sin(angle) + this.y * Math.cos(angle)
return Math.atan2(this.y - vector.y, this.x - vector.x);
var leftMax = this.parent.pos.x - this.parent.radius,
rightMax = this.parent.pos.x + this.parent.radius,
topMax = this.parent.pos.y - this.parent.radius,
bottomMax = this.parent.pos.y + this.parent.radius;
if (leftMax < -WIDTH/2 || rightMax > WIDTH/2) {
if (topMax < -HEIGHT/2 || bottomMax > HEIGHT/2) {
var differenceVector = object.pos.getShifted(this.parent.pos.getScaled(-1)),
distanceSquared = differenceVector.magnitudeSquared, distance, gammaVector;
if (distanceSquared > (this.parent.radius + object.radius)**2)
distance = Math.sqrt(distanceSquared);
differenceVector.scale(1 / distance);
gammaVector = differenceVector.getScaled(
2 * Vector2D.dot( differenceVector, object.vel.getShifted(this.parent.vel.getScaled(-1)) ) / (this.parent.mass + object.mass)
object.vel.shift(gammaVector.getScaled(-this.parent.mass));
this.parent.vel.shift(gammaVector.getScaled(object.mass));
var correctionDistance = (this.parent.radius + object.radius) - distance,
correctionVector = Vector2D.fromPolar(correctionDistance, this.parent.pos.angleBetween(object.pos)),
thisPortion = this.parent.mass / (this.parent.mass + object.mass);
object.pos.shift(correctionVector.getScaled(thisPortion - 1));
this.parent.pos.shift(correctionVector.getScaled(thisPortion));
for (var i = 0, object; i < objects.length; i++) {
if (object === this.parent)
for (var i = 0, object, differenceVector, force; i < objects.length; i++) {
if (object === this.parent)
differenceVector = object.pos.getShifted(this.parent.pos.getScaled(-1));
force = differenceVector.getScaled(G * object.mass * this.parent.mass / differenceVector.magnitudeSquared);
this.parent.applyForce(force);
constructor(pos, vel, mass, radius) {
this.collider = new Collider(this);
this.gravitator = new Gravitator(this);
this.vel.shift(force.getScaled(1 / this.mass));
this.collider.update(GC.particles);
this.gravitator.gravitate(GC.particles);
this.pos.shift(this.vel);
class Particle extends Object2D {
ellipse(this.pos.x, this.pos.y, this.radius*2, this.radius*2);
var speed = WIDTH / 400, angle, massMax = 2e10, mass;
for (var i = 0; i < PARTICLE_COUNT; i++) {
angle = Math.random() * 2*Math.PI;
mass = Math.random() * massMax;
this.particles.push(new Particle(
new Vector2D((Math.random() - 1/2) * WIDTH*0.9, (Math.random() - 1/2) * HEIGHT*0.9),
Vector2D.fromPolar(speed, angle),
Math.pow(mass, 1/10) * HEIGHT / 800
for (var i = 0, particle; i < this.particles.length; i++) {
particle = this.particles[i];
var GC = new GameController();
createCanvas(windowWidth, windowHeight);
background(60, 200, 255);
translate(windowWidth/2, windowHeight/2);