No matter if you are a designer, a developer or just an ordinary Internet user, once in your life you must have come across a website that used some 3D elements or maybe was a full-fledged 3D experience on its own. In this article we will dive together into world of 3D graphics on the web and make a first step to make you fall in love with it.
We will use a library called Three.js and we’ll start with a simple “hello world” of it.
So, in this tutorial, you’ll learn:
Three.js opens new possibilities for developers as well as designers and really changes the way we see web apps. It is used increasingly and its popularity grows continually. With it you can extend your user experience with some previously unavailable features. Not only you can add single eye-catching 3D elements to your website, but also you can create a whole film-like experience to make users save it in their bookmarks and come back when they need to see something truly amazing.
But third dimension is not only about appearance. Of course, it attracts users, but it also helps to make user interactions more interesting, to create new ways to use the app, to visualize some data and generally make your app easier to use.
If you want to learn more about tech evolution and how it allowed developers to create amazing web experiences, check out my previous article – Three.js – how tech evolution let us create 3D graphics on the web.
But before we’ll be able to do everything that we can imagine, we must start from learning this technology. I admit it’s really, really hard to learn all things that it offers, but the satisfaction after you create something is enormous.
One thing that I can recommend you throughout this article is that you should have the Three.js documentation opened and look at it while we discuss individual topics. It is important, as we can only learn some basic things in this article, so you should look at it by yourself to learn more.
We’ll start with creating simple project structure, installing needed packages and starting the development server. For our work we will need just the basic files, so in your project folder create three files: index.html, style.css and index.js
Then, we need to install the Three.js library using npm (or yarn if you prefer). In the console we need to use the following command:
Lastly, we need to start development server to see all the changes that we’re doing in the browser. To make it quick I’ll use bundler called Parcel (you can check it out here.) To run index.html file using Parcel we need to use following command:
That way we should have a basic development server with hot reloading started. We can see our work in a browser using url provided by Parcel in the terminal. Of course, right now we can’t see anything as we don’t have anything in our files, so let’s change it.
First we need to create basic HTML structure in index.html file (you can copy and paste it or use Emmet extension to do it) and we need to link CSS and JS files in it. To make it possible for parcel to bundle our code we need to add type=”module” to the script tag. Then, to work with Three.js we need one more thing in our HTML – canvas element.
Now that we have our HTML file finished, we can add some styling. Right now our canvas have some fixed width and height and we want it to be full screen and fixed to the browser window. We can also remove all scrolling behaviour by setting body overflow to hidden.
We will also remove margins and paddings from all elements, then the canvas element won’t have blank spaces between it and the browser window. In the end the CSS file should look like this:
Next we need to have our canvas element available, so let’s use querySelector to get it from our HTML:
Alright, now we can create the scene. Scene is an object where all other elements exists. Everything that we see in the final render is usually contained in some scene, so it’s something like a root container for other things.
To better understand how Three.js works we can think of our scene like a movie plan. The scene object is our plan. On it, we can put our objects, which also exists in Three library – camera, lights, meshes (actors) and so on. We will go through every of it, but have in mind that this comparison to movie plan is pretty accurate and we are directors in some way. Anyway, to create a scene we’ll use:
Now we can use our scene object to put other objects inside of it.
Right now, we don’t know yet what is happening on the scene, because we can’t look inside of it. So first, we need to put a camera in it, which will work as our eyes inside the scene. In Three.js we have multiple cameras to choose from, but the most used ones are:
In our project we will use the latter, to create proper 3D experience.
The perspective camera constructor takes 4 arguments, and it’s very important to know and understand all of them:
All objects on the scene that are in the range from near plane to far plane will be visible in final render, everything above far or below near won’t be rendered.
All of that is very well explained by this image:
So now, we’ll create our camera using the following parameters and then add it to the scene, as every object should be:
Once we have a camera on our scene, we can finally render it. For that, we’ll use object called renderer. Its job is to analyze everything on the scene – positions of objects in relation to another objects, to the world coordinates etc. and display it on our canvas, taking all this properties into account. Let’s create it:
As a parameter we provided object with our canvas element, so renderer will know where to display our scene. We also set the anti alias property to true for the final objects to be smoother. Then we set its size, so it renders the scene on a whole area of the canvas and set pixel ratio, so everything will be displayed properly even on screen with higher pixel density. We also changed the background color of the renderer to be gently lighter than default black.
See how we used a hexadecimal value of a color. It’s the recommended way to use it, but you can also use the Three library Color constructor, to use other types of color values. Read about it in the documentation.
Then, we need to use render method of renderer to display the scene. It takes two arguments: the scene which will be rendered, and the camera that will be used to render it. So basically it’s like saying: “Hey renderer! Use the image of my scene from this camera and display it on the canvas”.
That’s it! We can look in our browser and see… nothing? Yes, that’s our scene. It’s completely empty, but don’t worry, now we’ll add an actor.
Our actor will be a box. It is created using mesh object using two other objects – geometry and material. We can use objects created from Three library geometries and materials or the ones that we created in some 3D graphics software, like Blender. The latter are generally more complicated so we are not able to create them from native Three.js objects.
Geometry is like a skeleton, a shape of an object (created from vertices, edges and faces). We can use geometries included in the Three.js library (plane, box, sphere and others) or they can be custom made. You can see all geometries offered by Three.js in its documentation, where you can find a sandbox for every geometry, control all parameters and see how they change the shape of geometry, so I highly recommend to try it.
The other element that builds the mesh is material. It’s like a skin of a geometry and it determines how the object will look on the scene, how it will interact with the lights and so on. Materials have many parameters and every one of them is different, so you can check it in the documentation. With material we can use textures, height maps, normal maps and many other.
As our object we will use the BoxGeometry, which as you can see in the documentation takes six arguments: width, height, depth, and the number of segments for width, height and depth. We’ll use only first three of them and leave the rest as default.
We will also need a material. As some materials are physically correct and require the source of light to be seen, for our example right now we’ll use MeshBasicMaterial as it can be seen without the light on the scene. Lastly, we’ll create mesh from this two objects and add it to the scene. It’s very important that you add those lines before instantiating the renderer, so it can take this mesh into account while rendering:
Before we can continue, you may be concerned about values that we used in box geometry constructor. In Three.js as units we use… units – for us that can be meters, kilometers, pixels or some other values, but for Three.js they are just some units in a 3D space. So the good way of handling them is to establish some basic unit and make everything relative to it.
Ok, we have our actor on the scene, but it’s still empty. What is happening here? The answer is simple. Let’s remember that we are in a 3D space with x, y and z axis, so it looks something like this:
By default, every element added to the scene have the following coordinates: x = 0, y = 0 and z = 0. Right now our camera and object are in the same place of a scene, so we are „filming” our scene from inside of our cube!
The conclusion is simple, we need to move something, either camera or object. We’ll move camera on Z axis so it’s away from the cube. Add this line below the camera constructor and above adding the camera to the scene:
Yay! We have our object! But it’s flat… That’s because we are looking forward at it. Let’s move the camera on other axis and make it look at the point where the cube is, so the center of the scene (0,0,0). Change the previously written line of code to the following:
Yeah, that’s the proper way to see a cube. Finally we can see something that looks like 3D! Isn’t it cool? Let’s move on, next station – lights.
There are a lot of lights in Three.js and you can refer to documentation to know all of them. We add them to the scene like any other objects, but their configuration is different thing, so it’s really a matter of experimenting with their properties.
For our example we will use two lights:
To use lights, first, we’ll need to change the material of an object to the one that reacts with the light, so let’s change it to standard material:
Then, we can add lights. Just like with mesh you need to do it before instantiating the renderer:
Looks good, isn’t it? But it can be better! Let’s spin it!
Right now, we’re rendering our scene only once – when we use render method of a renderer. We’d like to make our cube spin, so we must render our scene multiple times, every time adding some value to the rotation of the cube. Ideally we would like to render our scene about 60 times per second and every time spin it a little, to make a smooth animation.
Luckily, we have the function that is created exactly for this and it’s available on the window object. It’s called requestAnimationFrame and it can be used to call animation function ideally in 60 frames per second (generally matching the display refresh rate). This function as an argument takes callback function that should be called before the next repaint.
Ok, so to use it we’ll create a function called animate that will call render method and will be called recursively using requestAnimationFrame. That way we will call render method multiple times in a second. Also, in every frame (every time the function is called) we’ll change the rotation of a cube a little, so at the end we will have a smooth, rotating cube. Let’s do this, change renderer.render(scene, camera) line to the following:
And we’re done! Watch it spin.
If you have some problems with your cube you can find the full code here: https://github.com/pati-gorrion/threejs-basics
Actually we are not done yet. There’s plenty to do here. You can try to add orbit controls to the scene, to allow you to control the camera with your mouse, or add some resizing to the window. You can also add some other objects to the scene, so our box is not alone, or just try to change some properties and watch how it changes the final render. Maybe try some other materials or lights? Just saying.
The scene is yours limited only by your imagination. You can find everything you need in a Three.js documentation and I really recommend to see it.
As you can see, in 3D graphics on the web even very basic example can give a lot of fun (at least I hope so). And from there, you can experiment with everything and watch even cooler things happen.
Every journey starts with a small step, so don’t give up and try something creative. The web is full of examples, you just need to find them. Of course, it’ll be hard in the beginning, but it’ll be very satisfying when you finally get it, you’ll see. And most importantly, it’ll let you do beautiful websites and apps to please the eyes of the others.
Other worthy reads