Immersive ride with Three.js

Ashutosh Hundekar
Globant
Published in
9 min readDec 1, 2020

As Web developers, we are always curious to know what a browser can do more than just creating a traditional application.

It’s been a long time since I have been evaluating the JavaScript libraries to see the true potential and capabilities of the browser and how we can deliver a great immersive experience.

By the end of this article, you will have a solid understanding of the basic building blocks necessary to add that extra dimension to your next web project.

But wait, what is Immersion?

Immersion means involvement in something. It’s about being able to connect with what you are seeing. It relies on two technologies: Augmented Reality (AR) and Virtual Reality (VR).

To define these two terms in short, Augmented Reality means placing the virtual object in our real world and creating an interactive experience. The clearest and most mainstream example of this concept is Pokémon Go.

Whereas Virtual Reality means stimulating a virtual environment itself. It is fully immersive and everything we see is part of an environment artificially constructed through images, sounds, etc. This environment is perceived through a device known as Virtual Reality headsets like Google’s Daydream, Oculus Rift, Samsung Gear VR, HTC Vive, and Windows Mixed Reality Headsets. VR experiences can also be surfaced in the browser.

Amazing, ain’t it?
Now let’s see one of the many tools which can help us create these experiences — Three.js. Three.js is one of the JavaScript libraries which we will use today to create immersive web apps.

Three.js is a JavaScript library that tries to make it as easy as possible to get 3D content on a webpage. Three.js uses WebGL to draw 3D images. WebGL is a very low-level system API that only draws points, lines, and triangles.
Considering that you already know JavaScript let's start with the basic fundamentals of the Three.js. Please check out the prerequisites here.

Fundamentals of the Three.js

Main Concepts of Three.js

Scene — This is a holder for everything else. You can think of it as a ‘tiny universe’ in which all your 3D objects live.

Camera — This is directly equivalent to the concept of a camera in the real world, and you’ll use this to view your scene. At what angle the 3D object should be kept, how far and near distance should be maintained, we can handle all these things with the help of the camera.

Renderer — This takes a Camera and a Scene as input and outputs beautiful drawings onto your <canvas> so that it can be viewed in the browser.

Canvas — This is an HTML canvas element, and just like a canvas in the real world, it starts as a blank rectangle, just waiting to hold your beautiful creations!

Together, the Scene, Camera, <canvas>, and Renderer give us the scaffolding of an app, however, none of them can be seen. We’ll also need to add some kind of visible object.

Geometry/Object — We can draw the things in Three.js with the help of geometry or you can directly load model or objects in 3D.

Material When any 3D object is drawn or added, it needs some appearance like color, shadow, opacity, gradient, and many more. This could be handled through Material.

Mesh — This is just a holder for a Geometry and Material and defines the position in 3D space. We can add this to our Scene.

To see how cool and amazing the results Three.js can give you, let's take a look at this http://carvisualizer.plus360degrees.com/threejs/ example which is a 360-degree car visualizer. Trust me this is a very basic example, Three.js has a lot more superpowers.
In the above example, there is a 3D model of a car that can be customized and even the model can be changed. Cool right?

Where to get these 3D models from? You can get them from here.

Let's build something amazing. We will create the following example of the sofa as a 3D model, the wooden floor, and adding some interactions to it.

Let’s first create a folder having the name ThreeJS Example and then create an index.html in it. This file will have basic stuff for adding CDN links for Three.js, GLTFLoader.js, and OrbitControls.js. GLTFLoader.js brings in the ability to load custom 3D objects in the .gltf file format. OrbitControls.js helps to activate all the rotating, dragging, and zooming controls we need for both mouse and touch. Also, let’s add the index.js file where we will write our main functions.

Also, let's add the app.css file and add these CSS rules.

Now, add the following line in the index.js to create our scene.

const scene = new THREE.Scene();

Let’s add some background color to the scene

scene.background = new THREE.Color(0xf1f1f1);
scene.fog = new THREE.Fog(0xf1f1f1, 20, 100);

Having our scene ready, add the canvas element so that it can hold the objects.

const canvas = document.querySelector("#sofa-canvas");

We have already added this HTML element in the index.html having the id as sofa-canvas.
Let’s add the camera.

let camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 5;
camera.position.x = 0;

Three.js provides two types of the camera PerspectiveCamera and OrthographicCamera. We are using PerspectiveCamera in our example.

Having the Scene and the Camera-ready let’s add the renderer by which the Scene can be viewed in the browser.

const renderer = new THREE.WebGLRenderer({ canvas, antialias: true });
renderer.shadowMap.enabled = true;
renderer.setPixelRatio(window.devicePixelRatio);
document.body.appendChild(renderer.domElement);

Now let’s add some controls for our object.

let controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.maxPolarAngle = Math.PI;
controls.minPolarAngle = 0;
controls.enableDamping = true;
controls.enablePan = false;
controls.dampingFactor = 0.1;
controls.autoRotate = false;
controls.autoRotateSpeed = 0.2;

If we set autoRotate as true and autoRotateSpeed then our sofa object will rotate automatically.
To animate our model following methods will help us.

To set the initial structure of our object, the following method will help us. Before that first create the default structures for the sofa and the pillows with the following constants i.e. DEFAULT_SOFA_TEXTURE and DEFAULT_PILLOW_TEXTURE.

const DEFAULT_SOFA_TEXTURE = {texture: './textures/denim_.jpg',
size: [3, 3, 3],
shininess: 0
};const DEFAULT_PILLOW_TEXTURE = {texture: './textures/cloth.jpeg',
size: [6, 6, 6],
shininess: 0
}function setInitialSofaMaterial() {const sofaMaterial = getTextureMaterial(DEFAULT_SOFA_TEXTURE);
setMaterial(theModel, sofaMaterial, "sofa");
const pillowMaterial = getTextureMaterial(DEFAULT_PILLOW_TEXTURE);
setMaterial(theModel, pillowMaterial, "pillows");
}

Here we have added the paths for the images for which you can create the folder named textures. These are some required assets which you can find here.

Also, we have called one method getTextureMaterial for getting the material for the sofa and the pillows.

This will simply return the material texture based on the color and it will be used by the setMaterial method as below.

For better segregation let’s create a utils folder. Then create materialHelper.js file and add getTextureMaterial and setMaterial methods in it.

Now, we need something that will load our 3D object. GLTFLoader will help us to load the 3D object inside our scene. For that, we will need the modal. You can find it here.

So far we have added the object into our scene but you will see it in black color. To enlight it we will have to add the lights.

Let’s add the following lines to our index.js file.

let hemiLight = new THREE.HemisphereLight(0xffffff, 0xffffff, 0.61);
hemiLight.position.set(0, 50, 0);
scene.add(hemiLight);

By now our object will look like this:

To see our object more clearly, we will have to add directional light.

let dirLight = new THREE.DirectionalLight(0xffffff, 0.54);
dirLight.position.set(-8, 12, 8);
dirLight.castShadow = true;
dirLight.shadow.mapSize = new THREE.Vector2(1024, 1024);
scene.add(dirLight);

Now you will be able to see the object more clearly. Hence, lights are important in the case of the visuals.

Let’s add the floor with the help of the following code.

floor.receiveShadow method will add the shadow for the object.

As of now, we have added the floor and we have built like below:

It's kind of having static colors. Let’s add some interactions for changing the colors for the sofa cushion and the pillows.

Let’s create a folder named constants and then add constants.js file into it and create an array of objects containing different textures and colors. You can find all that data here.

Once this is done let’s add one method selectSwatch in our materialHelper.js file where we have all our utilities which we have created in the earlier part of this article.

Now let’s add some HTML elements for our color tray and the options to select.

Also, let’s add some CSS rules in the app.css file. You can find those here.

You can find the required images for the sofa and pillows here. Also, you can find the textures required for our tray here.

Now we are ready with all the required things for adding the color tray and the options.
First, import all the colors and textures that we have defined already.

import { texturesAndColors } from "./constants/constants.js";

Let’s add some methods to set up all the colors and textures for our palette.

We have our tray ready. But we are not able to change any color or texture pattern of the sofa yet. So for that, we need to add the following code into our index.js file.

Hang On! We are nearly there!

You can observe that we are calling the method selectSwatch.
We have already added this into our materialHelper.js file.

We need to import it into the index.js file.

import { selectSwatch } from "./utils/materialHelper.js";

With help of this, we will be able to select any swatch and change the color or texture pattern of the sofa.

Now, the last part is interacting with the pillows with the help of the same color and texture tray. We can build the two options — a sofa and pillows.
We can add the below code to the index.js file.

When we click on the pillow option and select any color from the tray our model will change accordingly.
Cool, isn’t it?

Our model and the color tray is ready!!

Just one more thing to go!

We can show the loader so that before loading the model into the browser. You can find the final code here: https://github.com/Mr-10/ThreeJS

As you have made up to here there is a surprise for you and surely this will motivate you more to start working with Three.js.

So far we were developing code in plain vanilla JS, and sometimes it is very difficult to scale up due to many factors. Now with modern web development, we are very fortunate to have advanced architecture such as component/module based development.

What if we can use those advanced concepts while developing the Three.js application, ultimately everything is javascript!

So here you go!

You can develop a Three.js application in any of your favorite frameworks/libraries, and here are the links.

Congratulations, you have entered into the immersive world!!

Summary

Three.js is used to create 3D content on a browser, providing you with the power to display incredible models, games, scientific and data visualizations, or pretty much anything else you can imagine, right in your browser and on your smartphone!

It uses WebGL, a JavaScript API, which lets us access a computer’s specialized graphics hardware using JavaScript.

I hope with this article you have learned something beyond traditional web development and trust me this is the future!

If you found this useful, or have any suggestions, please let me know in the comments. Also, feel free to reach out to me on Twitter or LinkedIn.

Wish you happy learning and keep exploring the immersive world!

--

--