Since beginning my journey into the Front End, I have always been interested in how to create 3-dimensional renderings in the browser.
Somewhat related, I’ve recently also been exploring the creation of augmented reality experiences in React Native… but more on that later.
To accomplish this we will be constructing a fun, 3D, not-to-scale rendering of our solar system!
I say not-to-scale because, well, space is vast.
It's going to take the SpaceX Starship 6 months to travel to Mars in 2024, a distance which takes light a full 3 minutes.
If we make our little 3D browser-rendering to scale, our planets would be infinitesimally tiny in a black void...
Just like in real life! 🤣
No, instead we shall aim for visually pleasing over real-world accuracy.
Here is a gif of our finished project in the browser:
And here is the final project repository.
Let’s have some fun.🚀
Compared to modern web tech, WebGL (based on OpenGL) is relatively old.
Created in 2006 and adopted by Mozilla and Opera a year later, WebGL has been steadily iterated upon over the years and is now a standard tech included in modern browsers.
ThreeJS, the primary tech we'll be using today, is a library that allows us to create and display animated 3D computer graphics in the browser.
Built on WebGL, it allows us to create shapes, load textures, customize lighting, manipulate and animate perspectives, customize the orbit controls of the camera... among many other possibilities.
Today we are going to use an experimental library called canvas-sketch by Matt DesLauriers.
Check out his course on FrontEnd Masters if you feel so inclined.
This library will handle the heavy lifting of setting up ThreeJS for us, so we can simply focus on building.
First off, we will need to get set up with the
In your terminal, install it by entering the following:
> npm install canvas-sketch-cli -g
Once installation is finished, create a folder where you'd like to store this project and move into it.
I'll name this project
solar-system-threejs and throw it onto the Desktop:
> cd Desktop > mkdir solar-system-threejs > cd solar-system-threejs
With the canvas-sketch CLI installed, let's run a command to scaffold our ThreeJS project in this folder we created.
> canvas-sketch sketch.js --new --template=three
The setup here is very quick, and will finish with a server running on port
Open your browser and head over to
localhost:9966 and you will see the following 3D sphere wireframe.
In the future, to launch your project you will simply enter
canvas-sketch followed by the entry file.
Oh, one last thing!
ThreeJS allows us to create spheres to be our planets, but we need to provide specific images that map to spheres in order for them to look like planets.
Head over HERE and download the planet images as equirectangular projections that I've prepared for us.
Store them in an assets folder within the main root directory of the project.
Cool! This is where we'll start.
To begin, we need place the light source at the center of the solar system.
In 3D space, we have 3 axes:
X, Y, and Z.
Therefore the center point in 3D space is
[0, 0, 0].
Try playing around with the perspective and axes on a 3D scene over here in an interactive guidebook (created by
canvas-sketch creator Matt DesLauriers) to gain an understanding.
ThreeJS comes with all sorts of helpers to help aid in constructing 3D renderings.
Let's begin by placing a
pointlight helper at [0, 0, 0], and creating a
grid helper to help us see our horizontal plane.
sketch.js, replace all of the code with the following:
This will result in a helper "diamond" at the origin to show us where our light is coming from, and a grid to show us our X axis.
Don't be overwhelmed. We will go through this boilerplate together.
In the mean time, you should see this:
Let's dissect the code above to understand what we are doing to set this scene up.
Imports & constants
Three.js, orbit controls and
settingsand screen dimensions.
WebGLRendererto set up the canvas element, as well as set the background color.
camera.position.set(0, 10, 30).
sceneis everything you see, and everything you create must be added to this scene.
point lightto the center at [0, 0, 0], which projects light from that point out in every direction.
These things are fairly straightforward, and not necessary to really dive deep into right now.
This article is long enough as it is, and you can always read the docs for each utility method in your own anyways.
Let’s build some planets!
With this basic scene created, we can now begin by creating objects and adding them to our scene.
To create a planet, we must first create a sphere geometry.
We can do this using the
SphereGeometry method from
Three.js, passing values for the
height of the sphere.
This geometry isn't the shape itself, but rather just the implementation of what the shape should be.
In order to create the shape and add it to the scene, we must pass this geometry object as a parameter to a new
A Mesh is an object that is constructed from the most primitive object in 3D land: the polygon.
To create our sphere, we will create a new mesh, passing in the geometry above as a parameter.
We can then set the position and scale of the mesh, and add it to our scene.
All together, update your code so that the sphere mesh is being constructed after the creation of the scene.
Now we have a white sphere on our plane:
Now let's make this the planet Mercury!
TextureLoader from Three, we will load the image of the equirectangular projection of Mercury as a texture.
With this texture we will then create a new
MeshStandardMaterial, which allows us to map that image to a 3D object, like our sphere.
Finally we pass this material as a second parameter when creating the Mesh.
Now we have a sphere that looks like Mercury.
You know what, let's add a little spin to this planet.
In the render return block of our main
sketch() function, add this line to rotate the planet on it's y axis:
Alright, making progress!
In case you encountered any issues, here is the code up to this point.
Next we are going to apply the same process to create and position the other 8 planets.
(Yes this includes our old friend Pluto).
To recap the above section, our process for creating a planet is:
Let's run through this again with our next 2 planets from the Sun: Venus and Earth.
Great, we've added 2 more planets!
But we have run into 2 problems.
Let's start with the latter.
To rotate each planet around the origin
[0, 0, 0], we must place each planet in a sort-of container called a Group.
We will amend our code to create a group, add the mesh to the group instead of the scene, and then add the group to the scene.
To show this works as we intend, let's add a rotation to the
mercuryGroup above the rotation we added for the
Feel free to remove the grid helper at this point.
Now let's write a function to generate planets so that we don't repeat ourselves so much...
... and use this by passing in the params for each planet.
Since everything repeats for each planet, I don't see the need to walk through them individually.
The values I am using for scaling the planets and positioning them in space are very much ad hoc, my goal being to just create something visually pleasing while ensuring the planets are smaller/bigger in relation to each other.
Update your code with the following to add all the planets, as well as the individual rotations and orbits around the center.
Take your time with the code above.
Play around with the positioning in space, the scale, the rotations.
Have a little fun with it.
We are almost finished!
Last up is to create the Sun.
With all these new planets, let's first reposition the camera a bit along the x-axis so that we are centered to the scene rather than the origin.
Update the camera and orbit controls like so:
Now, the Sun☀️.
Up to this point we have been using the
PointLightHelper to show us where our light is coming from.
However, what we want is to see a big, glowing sun there instead.
In the same way that we created the planets, so too shall we create the Sun.
The only difference this time is that the Sun does not need to be in a Group because the Sun is not rotating around another object; it is the object.
So, again, let's load the
texture for the Sun, create a
sunMaterial, create a
sunMesh, and add it to the
(Just add each sun-related line of code above that of Mercury)
And now we have the Sun added to our scene at the center of the solar system!
Why is the Sun a big black ball?
I'll tell you why!
This is happening because the
pointLight we are using is within the Sun and projecting outwards.
Therefore, while our planets are lit, no light is being cast on the Sun itself, and therefore the sun is completely dark.
To hack this such that our sun is illuminated, we are going to add a bunch of spotlights pointing towards the origin.
We also have to make sure these spotlights are between the Sun and the first object, Mercury, so as to not light Mercury or the other planets unnaturally from both sides.👻
Under lighting, we are going to add a new spotlight, and as well as the
SpotLightHelper, for the time being.
The Spotlight takes 4 parameters: color, intensity (of the light), distance (when the light ends), and angle.
SpotLightHelper, we are able to see where we have positioned the light source (25, 0, 0), the angle of the light, and the distance the light travels.
With this we have illuminated 1 side of the Sun only.
Now, treating this like a cube, we will illuminate the other 5 sides.
I wrote a function called
createSpotlights to simplify this and keep the code DRY, so let's add it after the function
createPlanets , and call it from the
LIGHTING section by passing in the
Now let's take a look at our big, beautiful Sun.
With everything in place, let's comment out the helpers, uncomment the rotations, and add a rotation for the Sun!
This will bring our final code for this project to this:
It's a big chunk of code, but after walking through it I hope it is a bit more digestible.
Here is our Solar System in action.
A lot of code, though mainly due to the repetitive nature of creating 10 very similar objects.
What did we learn today?
Want to learn more?
I'd highly recommend checking out Matt DesLauriers' course on FrontEnd masters to gain some beginner knowledge of WebGL & ThreeJS.
Learning how to create objects in 3D space is very useful to creating games.
See you next time ✌️.