Basic setup
THREE.js docs:
- Installation β we are using Option 2
- Creating a scene
- Reference search
Running a local serverβ
Download Node.js and then run
npx serve .
in the directory you've set up for this course. You should see something like this:
ββββββββββββββββββββββββββββββββββββββββββββ
β β
β Serving! β
β β
β - Local: http://localhost:3000 β
β - Network: http://192.168.0.33:3000 β
β β
β Copied local address to clipboard! β
β β
ββββββββββββββββββββββββββββββββββββββββββββ
Copy that address into your browser.
Minimal sceneβ
Here is an absolutely minimal scene.
Let's walk through what's going on here.
index.htmlβ
<script src="./scene.js" type="module"></script>
This marks the file scene.js as an ES module. ES modules are the modern way to break JavaScript code into reusable chunks and share them. Specifically, this is what lets us use the import syntax in bases.js.
This is also what necessitates running a local server: for security reasons, ES modules can't be run in file:// mode.
<script type="importmap">
{
"imports": {
"three": "https://cdn.jsdelivr.net/npm/three@0.182.0/build/three.module.js",
"three/addons/": "https://cdn.jsdelivr.net/npm/three@0.182.0/examples/jsm/"
}
}
</script>
This is an import map. This tells the browser how to resolve "three" to an actual JS file. Without import maps, we would have to write
import * as THREE from "https://cdn.jsdelivr.net/npm/three@0.182.0/build/three.module.js";
which is harder to read and maintain.
scene.jsβ
Now let's take a look at the JavaScript.
import syntaxβ
import * as THREE from "three";
This is a modern import statement. It loads all of THREE.js into a variable called THREE. Unlike older module-loading solutions, the variable THREE is not available outside of scene.jsβthis is one advantage of the new syntax.
Another advantage of the new import syntax is that it allows tooling to statically determine which imports you actually use. This enables bundlers (covered in the Framework part of the course) to perform tree shaking, meaning they get rid of code that you don't actually use. If you use a bundler, then it is more efficient (in terms of file size) to only import what you need:
import {
BoxGeometry,
Mesh,
MeshBasicMaterial,
PerspectiveCamera,
Scene,
WebGLRenderer
} from "three";
However, in plain JavaScript it doesn't make a difference. (Although browsers can do the equivalent of tree-shaking, most libraries are not structured in a way that would enable this, since downloading one big file is usually faster than making roundtrips for several small files.)
Boilerplateβ
Next we have some boilerplate that should be understandable from the comments. Here are the relevant documentation links:
I don't really know how to do proper camera work, I just copy the same settings between every project.
Adding objectsβ
The most interesting part is:
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
This is the typical pattern for adding objects to a THREE.js scene. Most objects in THREE.js are Meshes. Every Mesh has an associated BufferGeometry defining its shape and a Material defining its color/appearance. The specific ones we used here are:
If you change the MeshBasicMaterial to another material, such as a THREE.MeshPhongMaterial, the cube will disappear! This is because we haven't yet added any lights to our scene.
Refactored codeβ
Here is a refactored scene with boilerplate extracted and various niceties added. We will use this setup as the base for everything going forward.