Build a 3D Environment with Three.js
Prerequisites: HTML, CSS, JavaScript
Versions: Three.js r128
Introduction
Three.js is a JavaScript library that features 3D objects and views rendered on a web page. It builds on top of WebGL by adding functionality for visual aesthetics including:
- Effects such as lights and shadows.
- Materials for building shapes.
- Textures for adding details to those shapes.
Since its release in 2010, Three.js has been used by many developers and companies alike. Below is the GitHub homepage, which uses Three.js to render a globe. It rotates and emits interesting connections between different points. We can even change it’s rotation with our mouse!
In this article, we are going to learn how to build a 3D environment with Three.js. Inside of this environment, we will render a cube that rotates at a modest speed. Below is what our completed environment will look like:
The renderer
, Scene
, and Camera
It should be noted that the Three.js API uses a considerable amount of stage and camera projection terms to name classes, functions and parameters.
The renderer
object is the root of a Three.js program and carries two parameters:
- A
Scene
object that contains 3D objects, lights, cameras, etc. - A
Camera
, which is an object-based abstraction of a camera view that exists both inside and outside of scenes.
Scenes and their child elements make up the scenegraph, a tree-like representation of the parent/child-relationship between objects in the scene. Scenegraphs can also contain zero or more cameras.
Cameras use methods that utilize parameters named after terms in camera projection. The following terms define the observable “shape” (or frustum) of the camera:
fov
stands for field of view, which is the range of the observable world from a (camera’s) perspective at a given moment in time, measured in degrees.aspect
describes the width:height ratio of the to-be-rendered<canvas>
element.near
andfar
define the range of viewable space between the camera lens and the drawn objects.
With some terms out of the way, let’s begin building a 3D environment.
Step 1: Setting up and installing Three.js
We’ll begin by opening the terminal, creating a directory called /helloCube, and changing into it.
$ mkdir helloCube$ cd helloCube
Next, we will create the following files:
$ touch helloCube.html$ touch helloCube.js
Then, let’s install Three.js. There are two primary options for installing Three.js in a project:
- We could install it with npm and import as a Node module with
import
orrequire()
. - We could use
<script>
elements to import the package source code via a CDN link.
For this article, we are going to use a CDN link to install Three.js. Let’s head to the next step to add markup.
Step 2: Adding the HTML and connecting to our JS
Let’s begin this step by opening helloCube.html and add the following markup:
<!DOCTYPE html><html lang="en"><!-- HTML head --><head><title>HelloCube Three.js!</title></head><!-- HTML body --><body></body></html>
Our 3D environment will eventually render a <canvas>
element to the body. By default, most HTML elements are displayed at the “block”-level where they begin on a new line and take up as much width as possible. We should add some styles to the page so that the body is as tall as the screen being used and the canvas is horizontally and vertically centered. Next, we’ll add a pair of <style>
tags to the <head>
element and do the following:
- Set the
margin
andpadding
ofhtml
andbody
to 0. - Set the
min-height
of thebody
to100vh
to ensure the canvas is centered. - Set the
display
of thebody
toflex
. - Set
align-items
andjustify-content
tocenter
.
<!DOCTYPE html><html lang="en"><head><style>html,body {margin: 0;padding: 0;}body {min-height: 100vh;display: flex;align-items: center;justify-content: center;}</style><title>HelloCube Three.js!</title></head>...</html>
Let’s go ahead and save the file. Next, we will render the page on a browser with the following command:
$ open helloCube.html
When our page loads, it should look like this:
Lastly, in preparation for the next step, we’re going to connect our HTML to the Three.js library as well as our own helloCube.js file. Let’s add two pairs of <script>
tags inside the <head>
element. One of the scripts will import Three.js via CDN with the following url: https://cdnjs.com/libraries/three.js/r128
. In order to ensure that our Three.js connection can access elements in the <body>
after the DOM loads, lets add a defer
attribute to both <script>
tags.
The other will link the helloCube.html file with the helloCube.js file:
<!DOCTYPE html><html lang="en"><head><style>html,body {margin: 0;padding: 0;}body {min-height: 100vh;display: flex;align-items: center;justify-content: center;}</style><script src="https://cdnjs.com/libraries/three.js/r128" defer></script><script src="helloCube.js" defer></script><title>HelloCube Three.js!</title></head><body></body></html>
Our markup is now connected with Three.js and with our local helloCube.js file.
Let’s save and close the helloCube.html file. Next, we’ll proceed to build the actual 3D Three.js environment!
Step 3: Establishing the 3D environment
For the remaining steps, we will finish building our 3D environment in the helloCube.js file. Let’s open the file and define a function called create3DEnvironment()
. Then, we will execute it directly afterwards.
const create3DEnvironment = () => {};create3DEnvironment();
When the helloCube.html loads at the <script>
tag linking to the helloCube.js file, the create3DEnvironment()
method will be invoked. To access functions and class constructors from Three.js, we’re going to use the THREE
constant. We are going to create our root object — the renderer
— with the THREE.WebGLRenderer()
function:
const create3DEnvironment = () => {const renderer = new THREE.WebGLRenderer();};create3DEnvironment();
By passing nothing into THREE.WebGLRenderer()
, the renderer
will create a new <canvas>
element when in use. This is effectively our 3D environment! Although our 3D environment is technically now created, we still would like a <canvas>
in helloCube.html to communicate with the renderer
. There is more to the 3D environment than building the renderer.
Let’s move on to the next step where we will set up a Camera
object.
Step 4: Setting up a new camera
In order to “see” the objects we render in our environment, we need to create a Camera
object. We will be using a perspective projection by creating a new THREE.PerspectiveCamera()
and passing in the following values:
- A
fieldOfView
(integer) to set the range for what the camera can observe in the environment. - An
aspect
ratio (integer) of the camera’s height and width. - A space range that describes how
near
andfar
objects can be viewed by the camera (both integers).
...const renderer = new THREE.WebGLRenderer();const fieldOfView = 75;// Measured in degrees, not radiansconst aspect = 2;// The canvas default (300px-wide: 150px tall --> 2:1 --> 2)const near = 0.1;const far = 5;const camera = new THREE.PerspectiveCamera(fieldOfView, aspect, near, far);};create3DEnvironment();
We’ve got a new camera
going! Let’s move on to the next step to build a scene for our camera to see into.
Step 5: Creating a scene with 3D objects
The canvas inside the renderer
constant is where a Scene
object is set. Scenes are areas where 3D objects and effects, such as light effects, are stored. These objects are composed of geometric and material properties meshed together into one cohesive, 3-dimensional “shape”, like a cube or a sphere.
Let’s first create our scene
with the THREE.Scene()
method:
…const camera = new THREE.PerspectiveCamera(fieldOfView, aspect, near, far);const scene = new THREE.Scene();};create3DEnvironment();
We are now ready to add some 3D objects to our scene! The core pieces of a 3D object are:
- A geometry that defines the size and dimensions of the object.
- A material that defines the overall appearance of the object.
Let’s begin building the geometry for our cube.
The first thing to do is define a new THREE.BoxGeometry()
by passing in a width
, height
, and depth
. We’ll store it in a geometry
variable.
…const scene = new THREE.Scene();const width = 1;const height = 1;const depth = 1;const geometry = new THREE.BoxGeometry(width, height, depth);};create3DEnvironment();
Next, we’ll need to create a material
for the cube’s appearance. We can create it by using the THREE.MeshBasicMaterial()
method and passing in an object with a color
property and a value.
const geometry = new THREE.BoxGeometry(width, height, depth);const material = new THREE.MeshBasicMaterial({ color: 0xc2c5cc });};create3DEnvironment();
Lastly, we will create the actual cube
object. Let’s combine the geometry
with the material
by using the THREE.Mesh()
method. Then, we’ll use .add()
to add the cube
to the scene
:
const material = new THREE.MeshBasicMaterial({ color: 0xc2c5cc });const cube = new THREE.Mesh(geometry, material);scene.add(cube);};create3DEnvironment();
Let’s go ahead and save the helloCube.js file. It’s now time to see what our rendered cube finally looks like in the next step!
Step 6: Rendering the scene and camera
Let’s take a step back and look at what we’ve done so far with our 3D environment:
- We created a
scene
that contains 3D objects and effects. - We created a
camera
that “views” the 3D objects and effects. - We created a
renderer
to facilitate a renderable<canvas>
.
Next, we will use the renderer
’s .render()
method to create a 3D environment. We will pass in the scene
and camera
objects we built in the previous steps. They will be rendered through the returned <canvas>
element.
const cube = new THREE.Mesh(geometry, material);scene.add(cube);renderer.render(scene, camera)};create3DEnvironment();
We can then append the domElement
of the renderer
to the DOM:
renderer.render(scene, camera)document.body.appendChild(renderer.domElement);};create3DEnvironment();
And our rendered page should look like this:
Wait?! That looks more like a square than a cube! Let’s find out for sure by trying to move it in the final step.
Step 7: Animating the cube
In this last step, we are going to write an .animate()
method that will move the cube in the 3D environment that just build in the previous step:
…const animate = (time, speed=1) => {time *= 0.001; // converted to secondsconst rotation = time * speed;cube.rotation.x = rotation;cube.rotation.y = rotation;renderer.render(scene, camera)document.body.appendChild(renderer.domElement);requestAnimationFrame(animate)};requestAnimationFrame(animate)};create3DEnvironment();
Some of the code shown above is new and some is refactored. Here is a breakdown of what we just did:
- First, we defined an
.animate()
method that accepts two integers, atime
and aspeed
, which is defined with 1. - Next, we converted the
time
into seconds. - Then, we defined a
rotation
by multiplying thetime
by thespeed
. - We then assigned the
rotation
to thecube.rotation.x
andcube.rotation.y
coordinates to “rotate” the cube. - Next, we rendered the
scene
andcamera
- Then we appended the
renderer.domElement
to the DOM. - Right after that, we used Three.js’s
requestAnimationFrame()
method to make a recursive call to our.animate()
method to keep the cube constantly rotating. requestAnimationFrame()
is also used outside of.animate()
meant to start the rotation.
Our rendered page should look something like this:
Conclusion
There we have it! We just learned how to use the Three.js library to build a 3D environment. More specifically:
- We built a
renderer
that created ascene
that contains 3D objects. - We built a
camera
that “views” the objects in thescene
. - We created an
.animate()
method that renders thescene
andcamera
in the returned<canvas>
element.
This is only scratching the surface of what Three.js can do. Visit their official website to view other projects and learn more!
Resources
- Solution code: helloCube.html, helloCube.js
- Three.js documentation: https://threejs.org/docs/index.html#manual/en/introduction/Creating-a-scene
Author
'The Codecademy Team, composed of experienced educators and tech experts, is dedicated to making tech skills accessible to all. We empower learners worldwide with expert-reviewed content that develops and enhances the technical skills needed to advance and succeed in their careers.'
Meet the full team