I built my first Three JS project. Here’s what I learned.
Today, I’ll teach you how to create an introductory scene with Three.js which is probably the most useful library for you to work with, as it already has many useful functions pre-written, which would otherwise require a lot of effort.
Fundamentals
Three.js is a 3D library that tries to make it as easy as possible to get 3D content on a webpage. Three.js is often confused with WebGL since more often than not, but not always, three.js uses WebGL to draw 3D.
WebGL is a very low-level system that only draws points, lines, and triangles. To do anything useful with WebGL generally requires quite a bit of code and that is where three.js comes in. It handles stuff like scenes, lights, shadows, materials, textures, 3d math, all things that you’d have to write yourself if you were to use WebGL directly.
Getting started
Go ahead and initialise our new project using the CodePen playground or setup your own project on Visual Studio Code with the following file structure under your src folder.
ThreeJS Scene Template
|- /src
|- index.html
|- style.css
|- Avatar.png
|- spaceTexture.png
|- moonTexture.png
|- normalMap.png
|- script.js
Part 1: HTML
The HTML is really basic. Edit your `index.html` and replace it with the following code.
<canvas id=”bg”></canvas>
Part 2: CSS
Next step is to add the following styles and complete our `style.css` file. Just make sure the div that will wrap the canvas fits the document, and apply any size you want to your plane div element.
@import url(‘https://fonts.googleapis.com/css2?family=Rubik:wght@700&display=swap');html,
body {
height: 100vh;
}body {
background-color: #000;
color: #fff;
display: flex;
align-items: center;
font-family: ‘Rubik’, sans-serif;
margin: 0;
overflow: hidden;
}div {
position: absolute;
left: 0;
top: 0;
padding: 1em;
}
canvas {
position: fixed;
top: 0;
left: 0;
}
Part 3: JavaScript
Now we can implement our JavaScript logic to our ThreeJS setup like so.
import * as THREE from ‘https://cdn.jsdelivr.net/npm/three@0.126/build/three.module.js';
import { OrbitControls } from ‘https://cdn.jsdelivr.net/npm/three@0.126/examples/jsm/controls/OrbitControls.js';//creating the scene
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);const renderer = new THREE.WebGLRenderer({
canvas: document.querySelector(‘#bg’),
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);const controls = new OrbitControls(camera, renderer.domElement);
controls.enableKeys = false;
controls.maxDistance = 5000.0;
controls.enableDamping = true;
controls.dampingFactor = 0.16;
controls.target.set(0, 0, 0);// If you use THREE.OrbitControls and you want to change the camera target or position you need to use the update method.
camera.position.set(0, 5, 0);
controls.update();document.body.appendChild(renderer.domElement);THREE.ImageUtils.crossOrigin = ‘’;
const texture = THREE.ImageUtils.loadTexture(‘avatar.png’);
texture.anisotropy = renderer.getMaxAnisotropy();//Stars
function addStar() {
const geometry = new THREE.SphereGeometry(0.25, 24, 24);
const material = new THREE.MeshStandardMaterial({ color: 0xffffff });
const star = new THREE.Mesh(geometry, material);const [x, y, z] = Array(3)
.fill()
.map(() => THREE.MathUtils.randFloatSpread(300));star.position.set(x, y, z);
scene.add(star);
}Array(200).fill().forEach(addStar);// Lights
const pointLight = new THREE.PointLight(0xffffff, 2);
pointLight.position.set(5, 5, 5);const ambientLight = new THREE.AmbientLight(0xffffff, 1);
scene.add(pointLight, ambientLight);// Backgroundconst spaceTexture = new THREE.TextureLoader().load(‘https://media.istockphoto.com/photos/aerosol-clouds-space-haze-or-cosmic-rays-pink-pastel-blue-space-sky-picture-id1306540124?b=1&k=20&m=1306540124&s=170667a&w=0&h=Cpdu6iLmqYM7BFjoq0xvTnR6ws12ba6v7yxAFhCenyI=');
scene.background = spaceTexture;// Moon
const moonTexture = new THREE.TextureLoader().load(‘data:image/jpeg;base64,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’);
const normalTexture = new THREE.TextureLoader().load(‘https://www.filterforge.com/filters/9158-normal.jpg');const moon = new THREE.Mesh(
new THREE.SphereGeometry(3, 32, 32),
new THREE.MeshStandardMaterial({
map: moonTexture,
normalMap: normalTexture,
})
);scene.add(moon);moon.position.z = 60;
moon.position.setX(-10);//Torus
const ringGeometry = new THREE.TorusGeometry(10,3,16,100);
const material = new THREE.MeshBasicMaterial({color: ‘red’});
const torus = new THREE.Mesh(ringGeometry,material);
scene.add(torus);//cube
const cubeGeometry = new THREE.BoxGeometry(3, 3, 3);
let materials = [];
for(let counter = 0; counter < 6; counter++) {
materials.push(new THREE.MeshBasicMaterial({color: ‘white’, map: texture}));
}var cube = new THREE.Mesh(cubeGeometry, materials);
scene.add(cube);//loop
var animate = function () {
requestAnimationFrame( animate );
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
torus.rotation.x += 0.01;
torus.rotation.y += 0.005;
torus.rotation.z += 0.01;
moon.rotation.x += 0.005;
renderer.render(scene, camera);
};animate();
Cool! Now, if you save it, you should see this in the browser:
Recap
If you followed along then you should have completed the project and finished off your basic scene setup with ThreeJS.
Now if you made it this far, then I am linking the code to my Sandbox for you to fork or clone and then the job’s done.