HTML 5 Canvas: Solar System
By: Brandon Baars
Tags: Animation, Html5 Development, JavaScript, Javascript Development

Create a solar system with the Canvas API using Javascript

This is what we will be creating in this tutorial:

Everything you see is done using javascript. Each planet will have its own moon that will also rotate around it. (Completly avoiding the current solar system by giving every planet just one moon).

Background

I have only been learning the Canvas API for about a week or so and have watched some YouTube videos by Chris and on his website at https://chriscourses.com/ .

Let’s Get Started

To start off, download this canvas boilerplate made by Chris. It’s just a project setup to get the necessary HTML, CSS, and Javascript files out there.

Run npm install inside the directory where you have this boilerplate. This will install the necessary dependencies in order to get the project to run.

Next, in the same terminal, run npm start. Open a browser and go to localhost:3000. You will see the following:

So far the text just follows the position of the mouse. We will strip some stuff out and start with creating the Sun.

Open up js/canvas.js and change the file to look like the following:

A couple of things to note.

I removed the mouse event listeners and the mouse tracking object.

I changed the name of the class to be Planet and updated the constructor to take in a few extra parameters. (ones we will need when constructing our Planet objects.

The Sun

To get started, let’s construct our Sun object.

Back in canvas.js let’s add the following to the init function.

planets.push(new Planet(canvas.width / 2, canvas.height / 2, 35, 'yellow', 0, 0));

Here we are adding a planet (actually the sun is a star, but for the sake of this we will neglect that fact.).

We define the x and y to be the center of our canvas, the radius to be 35 and the color to be yellow. Since our Sun is stationary, it will not have an orbit radius or velocity.

Next, in the animate function, add the following code:

// Animation Loop function animate() {

requestAnimationFrame(animate); c.clearRect(0, 0, canvas.width, canvas.height);

c.fillStyle = 'rgb(0, 0, 0)';

c.fillRect(0, 0, canvas.width, canvas.height); planets.forEach(planet => {

planet.update();

});

}

We add a fill style of black and fill the canvas with our newly added fill style.

We then call the update function on each planet we add to our planet array.

Now you will see the sun shining brightly in the center of our canvas.

Now let’s move on to our first planet, Mercury, and understanding how we can make it orbit.

What we need to do is maintain a circular orbit at a certain radius from our canvas’ center. From there, we need to define how fast our planet will move around its center. For mercury, we want to update its velocity in the animation loop by 0.005. So, given a starting x and y pos of the planet, we can add the velocity to both of those in order to move it during each animation frame.

(Plot twist, this alone won’t actually get the circular pattern we need, but let’s run with it for now)

Add the following code back in canvas.js

in the init() function let’s add another planet to our planets array

// Mercury

planets.push(new Planet(canvas.width / 2, canvas.height / 2, 5, 'gray', 0.005, 65));

We are adding the planet again, starting at the center of our canvas and we define the radius to be 5 and the velocity to be 0.005 and the orbit radius to 65.

Now, add the following code in the Planet update() function,

update() {

this.draw(); // Update the x and y by the velocity

this.x += this.velocity;

this.y += this.velocity;

}

Here, in every animation frame, we are adding the velocity to our x and y. Over time the planet will move by that amount. It’s very slow.

Like 2 min have elapsed. But the plane has moved straight diagonally.

Let’s now learn how to make the planet move in a circle along the orbit radius which we have defined.

Using radians (as an angle), sine and cosine, we can constrain the planet to only move in a circle.

We can increase our ‘angle’ (again, in radians), by our velocity. This will slowly increase the angle over time. At each new ‘radian’, we can obtain an x and y coordinate using the cos and sin functions respectively. The corresponding x and y will now be constrained to a circle as it will give us the exact spot on the edge of the circle.

The result of these sin and cos numbers now between -1 and 1. This is considered our unit circle, so in order to make it a bigger circle, we just need to multiply by our orbit radius . One cool thing to

Try it yourself: https://www.mathsisfun.com/geometry/unit-circle.html

One small thing to note. We need to use our starting x position and starting y pos the entire time instead of doing this.x += Math.cos(rad) That will give unintended results and we want to only add the resulting x, y to our starting position.

Using these concepts, let’s go back now and edit our update() method.

But first, we want to make note of our starting x and starting y pos. Back in the constructor() of our Planet , add these two variables:

constructor(x, y, radius, color, velocity, orbitRadius) {

this.x = x;

this.y = y;

this.startingPos = {

x,

y

}

this.radian = 0;

... // Everthing else

}

Here we make note of the starting position that way when we update the x and y values constantly we aren’t losing the starting position. And we start with an angle of 0 radians.

Now in the update function, make the following changes:

this.radian += this.velocity; // increase our angle every animation frame

// Get the new x based on our new angle and radius

this.x = this.startingPos.x + Math.cos(this.radian) * this.orbitRadius; // Get the new y based on our new angle and radius

this.y = this.startingPos.y + Math.sin(this.radian) * this.orbitRadius;

Imagine the starting position is the top circle. We will increase our angle over time (by adding velocity to our radian) and get the corresponding new position (the bottom circle). Since I mentioned our values are -1 to 1, we can multiple by our orbit radius to get the new x and y.

As you can see our planet is behaving like a planet

The angle is constantly being updated and our radius of 65 is updating it to be just outside of our sun.

Let’s work on drawing the orbit circle that our planet will follow. This can be done in the draw function like so:

We are creating a new ‘arc’ which we will stroke with a line width of 2. The x and y will be our starting points for the path and the orbit radius is the radius we will use for the arc.

Now, that we have that out of our way, let’s add in our other planets!

Back in the init() add the following helper function and code:

I created a helper function to construct or Planet for us and added the remaining planets. You should see something like this now:

(Doesn’t show them animating, but they move trust me)

Using these concepts let's add a moon to each planet!

Thankfully each planet can just know about its moon and we don’t have to use any additional construction on our planets. All the moon data will be stored within the planet-class itself.

So, in the constructor() for the Planet , add the following code:

constructor(x, y, radius, color, velocity, orbitRadius) {

... // Exisiting properties



this.moon = {

x: this.x + this.orbitRadius + this.radius,

y,

radian: 0,

velocity: (Math.random() + 0.1) / 30

};

}

We define the position of our moon to be the current x position of our planet (since it starts at the center of the canvas)+ the orbit radius + the radius of the planet. This will put our moon right at the edge of the planet (in 2d).

Now, let’s draw our moon in the draw() function at the very bottom:

// Moon (not sun)

if (this.velocity > 0) {

c.beginPath();

c.arc(this.moon.x, this.moon.y, 2, 0, Math.PI * 2, false);

c.fillStyle = 'gray';

c.fill();

}

We only draw our moon if it’s not the sun. We know it’s not the sun by the velocity since our sun is at rest (I know this is a hack, but for the sake of the example I left it in)

We draw our moon with a radius of 2 it’s current x and y position.

Next, in the update function let’s animate our moon! Back in the update() function at the very bottom of it, add:

this.moon.radian += this.moon.velocity;

this.moon.x = this.x + Math.cos(this.moon.radian) * (this.radius + 5);

this.moon.y = this.y + Math.sin(this.moon.radian) * (this.radius + 5);

This uses the x and y of the current planet and animates the moon around it 5 px from the edge of the planet.

Now our planets and moon animate around our sun. Ahh, how beautiful.

Now let’s add in some Stars !

Let’s create a new Class one called Star and draw it.

The position of our star will be a random x and y coordinate within the size of our canvas. The size of the star (radius) will vary between 0 and 2.

Now, back in the init() function add the following:

Here we added the for loop to add 400 stars.

Next we just need to draw each star. In the animate function let’s loop over all the stars and draw them.

Make sure to add this above the planets.forEach()

stars.forEach(star => {

star.draw();

});

And that’s it!

Here is a link to the full source code.