ThreeJS Hello Cubes JavaScript Tutorial
This article covers how to create a 3D Web app with JavaScript using ThreeJS.
The app will display several 3D rotating cubes on the screen.
Step 1. Define the project
The project will be called threejs-hello and use the following components:
Folders
- src – a source folder to hold all the files and subfolders
- the root of the Web app will be src
- src/css – a stylesheet folder that will be a subfolder of src
Browser files
- src/index.html – the main browser file to display the scene
- src/css/app.css – the stylesheet for the Web app
Application files
- src/app.js – the main JavaScript file of the Web app
- src/cube.js – a cube factory class that will create new ThreeJS cubes on demand
- src/cube-scene.js – a cube scene factory class that will return a new ThreeJS scene containing a number of rotating cubes
Step 2. Create a new project folder
Now that you’ve defined the project structure, it’s time to set it up:
- Open up a terminal / command window
- Create a new folder called
threejs-hello
- Switch to that folder
- Under that folder, create a new subfolder called
src
- Under the src folder create a new subfolder called
css
Step 3. Create the project files
-
In the src folder create the following empty files:
index.html
app.js
cube.js
cube-scene.js -
In the src/css folder create the following empty file:
app.css
Step 4. Define the index.html file
Paste the following into the src/index.html file:
<html>
<head>
<title>threejs-hello</title>
<link rel="stylesheet" href="./css/app.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r122/three.min.js"></script>
</head>
<body>
<script type="module" src="./app.js"></script>
</body>
</html>
Code Description
The code above, does the folowing:
- Sets the title that appears at the top of the browser window
- You can make this anything you want
- Includes the css/app.css style sheet that you will define soon
- Includes the minimized ThreeJS library JavaScript file using a CDN network
- The /r22/ designation refers to the build version number
- Includes the src/app.js file that you will define soon
- It’s important to use the
type="module"
attribute or the code won’t load properly
- It’s important to use the
Step 5. Fill in the src/css/app.css file
Paste the following into the src/css/app.css file:
body {
margin: 0;
}
canvas {
width: 100%;
height: 100%;
}
Code Description
The css code above does the following:
- Sets all margins on the body to zero
- Sets the width and height of a canvas element created by the script to 100%
The settings are to allow a 3D scene to fill the entire browser window.
Step 6. Define the cube.js file
Paste the following info the src/cube.js file:
// File: cube.js
// Author: Mitch Allen
// 1. Define and export a cube factory class
export class CubeFactory {
// 2. Define a static create method to return new cubes
static create( spec = {} ) {
// 3. Setup default values or use arguments
let {
name = "cube",
color = "#FF00FF",
width = 1,
height = 1,
depth = 1,
x = 0.0,
y = 0.0,
z = 0.0,
} = spec;
// 4. Use ThreeJS to define a 3D cube
var geometry = new THREE.BoxGeometry(width, height, depth);
var material = new THREE.MeshBasicMaterial({ color: color });
var cube = new THREE.Mesh(geometry, material);
// 5. Use the name property to specify a type
cube.name = name;
// 6. Using ThreeJS methods on the cube, move it to a specific offset
cube.translateX(x);
cube.translateY(y);
cube.translateZ(z);
// 7. Return the new cube
return cube;
}
}
Code Description
The code above does the following:
- Defines and exports a cube factory class (CubeFactory)
- Defines a static create method to return new cubes
- The method takes an optional object (spec) defining override parameters
- Setup default values or use arguments
- If no spec object is passed to the method, default values will be used
- If the spec object contains existing parameters, the default values will be overwritten for that parameter
- Values for color, size (width, height, depth) and offset (translate[X,Y,Z]) can all be overridden
- Uses ThreeJS to define a 3D cube
- One way to create a 3D object in ThreeJS is to combine a geometry and a material to define a mesh
- Use the name property to specify a type
- Instead of assigning individual names to each cube, the name property will be used to designate a type (in this case “cube”)
- Use ThreeJS methods on the cube to move it to a specific offset from 0, 0, 0
- The default values are all set to 0.0, so the cube would be drawn in the 3D center of the scene
- Returns the new cube
Usage example
In this example, cubes of various colors, sizes and locations would be added to a scene:
var cubeOptions = [
{ color: "#FF0000" },
{ color: "#00FF00", width: 0.5, height: 2, depth: 0.5 },
{ color: "#0000FF", width: 2, height: 0.5, depth: 0.5 },
{ color: "#FF00FF", width: 0.5, height: 0.5, depth: 2 },
{ color: "#FFFF00", translateX: 3.0 },
{ color: "#FF6619", translateX: -3.0 },
{ color: "#AAAAAA", translateY: 2.0, translateZ: -0.05, width: 0.5, height: 0.5, depth: 2 },
{ color: "#04D9FF", translateY: -2.0, translateZ: 0.05, width: 0.5, height: 0.5, depth: 2 },
];
cubeOptions.forEach(options => scene.add(CubeFactory.create(options)));
Step 7. Define the cube-scene.js file
Paste the following info the src/cube-scene.js file:
// File: cube-scene.js
// Author: Mitch Allen
// 1. Import the cube class factory
import {CubeFactory} from './cube.js';
// 2. Define and export a scene factory class
export class CubeSceneFactory {
// 3. Define a static create method to return new scenes filled with cubes
static create( spec = {}) {
// 4. Setup default values or use spec arguments
let {
clear = "#000000",
// fov — Camera frustum vertical field of view
fov = 75,
// aspect — Camera frustum aspect ratio
aspectRatio = window.innerWidth / window.innerHeight,
near = 0.1, // near — Camera frustum near plane
far = 1000, // far — Camera frustum far plane.
} = spec;
// 5. Define a list of options for cubes to be created
var cubeOptions = [
{ color: "#FF0000" },
{ color: "#00FF00", width: 0.5, height: 2.0, depth: 0.5 },
{ color: "#0000FF", width: 2.0, height: 0.5, depth: 0.5 },
{ color: "#FF00FF", width: 0.5, height: 0.5, depth: 2.0 },
{ color: "#FFFF00", x: 3.0 },
{ color: "#FF6619", x: -3.0 },
{ color: "#AAAAAA", y: 2.0, z: -0.05, width: 0.5, height: 0.5, depth: 2 },
{ color: "#04D9FF", y: -2.0, z: 0.05, width: 0.5, height: 0.5, depth: 2 },
];
// 6. Setup a ThreeJS renderer to render the scene
var renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setClearColor(clear);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 7. Create a new ThreeJS scene
var scene = new THREE.Scene();
// 8. Create a ThreeJS camera
var camera = new THREE.PerspectiveCamera(fov, aspectRatio, near, far);
camera.position.z = 5; // Set camera position
// 9. Add several cubes to the scene using the cubeOptions list of parameters
cubeOptions.forEach(options => scene.add(CubeFactory.create(options)));
// 10. Define a cube scene with methods to return
var cubeScene = {
// 11. Define a method on the cube scene to handle browser window resizing
resize: function () {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
},
// 12. Define a method to be called when the scene is rendered
step: function () {
scene.traverse(function (cube) {
if (cube.name === "cube") {
cube.rotation.x += 0.01;
cube.rotation.y += 0.02;
}
});
renderer.render(scene, camera);
}
};
// 13. Return the new cube scene
return cubeScene;
}
}
Code Description
The code above does the following:
- Imports the cube class factory (CubeFactory)
- Defines and exporst a scene factory class (CubeSceneFactory)
- Defines a static create method to return new scenes filled with 3D cubes
- Sets up default values or uses spec arguments
- Defines a list of options for cubes to be created
- Sets up a ThreeJS renderer to render the scene
- Creates a new ThreeJS scene
- Creates a ThreeJS camera
- Adds several cubes to the scene using the cubeOptions list of parameters
- Defines a cube scene with methods to return
- Defines a method on the cube scene to handle browser window resizing
- Defines a method to be called each time the scene is rendered
- Traverses the scene looking for all objects with a name of “cube”
- If a cube object is found it’s x and y rotation parameters are incremented to animate the cube spinning
- Returns the new cube scene
Step 8. Define the app.js file
The main app.js file will do the following:
- Import the CubeSceneFactory class
- Create a new cube scene (cubeScene) using the factory
- Define a render function to handle drawing a frame of the scene
- Add event listener code to handle browser window resizing
Paste the following into the src/app.js file:
// File: app.js
// Author: Mitch Allen
// 1. Add reminder for how to reference the script
/*
In a browser must use script type="module" parameter:
<script type="module" src="./src/app.js"></script>
*/
// 2. Import the cube scene factory
import {CubeSceneFactory} from './cube-scene.js';
// 3. Create a new cube scene using the factory
var cubeScene = CubeSceneFactory.create({
clear: "#111111"
});
// 4. Define a render function
var render = function() {
requestAnimationFrame( render );
cubeScene.step();
}
// 5. Add and define a listener for browser resize events
window.addEventListener( 'resize', onWindowResize, false );
function onWindowResize(){
cubeScene.resize();
}
// 6. Call render to initiate the scene display
render();
Code Description
The code above does the following:
- Lists a reminder in the comments for how to include the script in an HTML file
- Imports a CubeSceneFactory from
./cube-scene.js
which you will define soon - Creates cubeScene from the factory using its create method
- The
clear
RGB (color) hex value will be used as the background color
- The
- Defines a render function that does the following:
- calls the ThreeJS requestAnimationFrame function
- calls a method on the cubeScene called step to rotate each cube a little for every frame
- Adds and defines a window listener to handle the browser being resized
- Calls the cubeScene resize method to draw frames based on the changed browser size
- Calls render to initiate drawing the screen
Step 9. Serve the files
To run the app you need to serve up src/index.html using a Web server.
On a Mac you can do that with this command:
cd src
python -m SimpleHTTPServer $PORT || 8000
Then in a browser like Chrome, browse to:
On a Mac you can also open the browser from the command line”
open http://localhost:8000
You can also host demo apps for free by posting them to services like:
If you have an AWS, Azure or Google Cloud account you can also serve the files from buckets as a static Web site.
Troubleshooting
- This demo works best in the latest version of Chrome or the Oculus Browser
- If you are getting a blank screen, use the browser JavaScript console to review issues
Conclusion
In this article you learned how to:
- Create a simple Hello World app using ThreeJS
- Define a class factory to generate cubes of various colors, sizes and positions
- Define another class factory to generate a ThreeJS scene filled with cube
- Use a render loop to animate cubes on the screen
- Serve the files so the 3D app can be viewed in a browser