NexusCS

Three.js

JavaScript libraries
Three.js is a JavaScript 3D library for creating and displaying 3D graphics in the browser using WebGL/WebGPU. This covers the most commonly used API patterns.
featured

Getting started

Installation

npm install three

Hello World

import * as THREE from "three";

const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
  75,
  window.innerWidth / window.innerHeight,
  0.1,
  1000,
);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

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);
camera.position.z = 5;

function animate() {
  cube.rotation.x += 0.01;
  cube.rotation.y += 0.01;
  renderer.render(scene, camera);
}
renderer.setAnimationLoop(animate);

Creates a rotating green cube with perspective camera and WebGL renderer.

Animation Loop

// Modern (preferred)
renderer.setAnimationLoop(animate);

// Legacy
function animate() {
  requestAnimationFrame(animate);
  renderer.render(scene, camera);
}
animate();

Delta Time

const clock = new THREE.Clock();

function animate() {
  const delta = clock.getDelta();
  const elapsed = clock.getElapsedTime();

  mesh.rotation.x += delta * speed;
  renderer.render(scene, camera);
}

Cameras

PerspectiveCamera

// PerspectiveCamera(fov, aspect, near, far)
const camera = new THREE.PerspectiveCamera(
  75, // FOV in degrees
  window.innerWidth / window.innerHeight, // Aspect ratio
  0.1, // Near plane
  1000, // Far plane
);

camera.position.set(0, 5, 10);
camera.lookAt(0, 0, 0);

Creates a perspective camera with realistic depth perception.

OrthographicCamera

// OrthographicCamera(left, right, top, bottom, near, far)
const camera = new THREE.OrthographicCamera(
  -10,
  10, // left, right
  10,
  -10, // top, bottom
  0.1,
  1000, // near, far
);

Creates an orthographic camera without perspective distortion.

Camera Methods

camera.position.set(x, y, z);
camera.lookAt(targetVector);
camera.updateProjectionMatrix(); // After changing properties

Geometries

Basic Shapes

new THREE.BoxGeometry(w, h, d);
new THREE.SphereGeometry(r, wSeg, hSeg);
new THREE.PlaneGeometry(w, h);
new THREE.CylinderGeometry(rT, rB, h);
new THREE.ConeGeometry(r, h, seg);
new THREE.TorusGeometry(r, tube, rSeg);

Advanced Shapes

new THREE.CircleGeometry(r, seg);
new THREE.RingGeometry(inner, outer);
new THREE.TorusKnotGeometry(r, tube);
new THREE.IcosahedronGeometry(r, detail);
new THREE.OctahedronGeometry(r, detail);
new THREE.TetrahedronGeometry(r, detail);

Custom BufferGeometry

const geometry = new THREE.BufferGeometry();

const vertices = new Float32Array([-1, -1, 0, 1, -1, 0, 0, 1, 0]);

geometry.setAttribute("position", new THREE.BufferAttribute(vertices, 3));

const mesh = new THREE.Mesh(geometry, material);

Creates custom geometry from vertex data.

Materials

Material Types

Material Lights Use case
MeshBasicMaterial No Unlit, flat color
MeshLambertMaterial Yes Matte, non-shiny
MeshPhongMaterial Yes Shiny, specular
MeshStandardMaterial Yes PBR, realistic
MeshPhysicalMaterial Yes PBR+, glass/coat

Material Properties

const material = new THREE.MeshStandardMaterial({
  color: 0xff0000,
  map: texture,
  normalMap: normalTex,
  roughness: 0.5, // 0=mirror, 1=matte
  metalness: 0.5, // 0=plastic, 1=metal
  transparent: true,
  opacity: 0.8, // 0-1
  side: THREE.DoubleSide,
  wireframe: false,
});

Common Properties

material.color.set(0xff0000);
material.needsUpdate = true; // After changing properties
material.dispose(); // Free memory

Lights

Ambient Light

const ambient = new THREE.AmbientLight(
  0x404040, // color
  1, // intensity
);
scene.add(ambient);

Global illumination, no shadows.

Directional Light

const dir = new THREE.DirectionalLight(
  0xffffff, // color
  1, // intensity
);
dir.position.set(5, 10, 7);
scene.add(dir);

Sun-like light with parallel rays.

Point Light

const point = new THREE.PointLight(
  0xffffff, // color
  1, // intensity
  100, // distance
);
point.position.set(0, 10, 0);
scene.add(point);

Bulb-like light radiating in all directions.

Spot Light

const spot = new THREE.SpotLight(0xffffff, 1);
spot.position.set(0, 10, 0);
spot.angle = Math.PI / 6;
spot.penumbra = 0.1;
scene.add(spot);

Hemisphere Light

const hemi = new THREE.HemisphereLight(
  0xffffbb, // sky color
  0x080820, // ground color
  1, // intensity
);
scene.add(hemi);

Shadows

renderer.shadowMap.enabled = true;
light.castShadow = true;
mesh.castShadow = true;
floor.receiveShadow = true;

// Shadow quality
light.shadow.mapSize.width = 2048;
light.shadow.mapSize.height = 2048;
light.shadow.camera.near = 0.5;
light.shadow.camera.far = 500;

Textures

Loading Textures

import { TextureLoader } from "three";

const loader = new TextureLoader();

// Async
const texture = await loader.loadAsync("texture.jpg");

// Callback
loader.load("texture.jpg", (texture) => {
  mesh.material.map = texture;
  mesh.material.needsUpdate = true;
});

Applying Textures

const material = new THREE.MeshStandardMaterial({
  map: loader.load("diffuse.jpg"),
  normalMap: loader.load("normal.jpg"),
  roughnessMap: loader.load("rough.jpg"),
  metalnessMap: loader.load("metal.jpg"),
  aoMap: loader.load("ao.jpg"),
});

Texture Settings

texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
texture.repeat.set(2, 2);
texture.offset.set(0.5, 0.5);
texture.rotation = Math.PI / 4;
texture.colorSpace = THREE.SRGBColorSpace;

// Filtering
texture.minFilter = THREE.LinearMipmapLinearFilter;
texture.magFilter = THREE.LinearFilter;

Wrapping Modes

Mode Description
RepeatWrapping Tile texture
ClampToEdgeWrapping Stretch edge pixels
MirroredRepeatWrapping Mirror and repeat

Objects & Transforms

Mesh

const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);

// Position
mesh.position.set(x, y, z);
mesh.position.x = 5;

// Rotation (radians)
mesh.rotation.set(x, y, z);
mesh.rotation.y = Math.PI / 4;

// Scale
mesh.scale.set(2, 2, 2);
mesh.scale.x = 1.5;

// Look at point
mesh.lookAt(new THREE.Vector3(0, 0, 0));

Groups

const group = new THREE.Group();
group.add(mesh1);
group.add(mesh2);
group.add(mesh3);
scene.add(group);

// Transform group moves all children
group.position.set(0, 5, 0);
group.rotation.y = Math.PI;
group.scale.set(2, 2, 2);

Object Hierarchy

parent.add(child);
parent.remove(child);
scene.getObjectByName("meshName");
scene.getObjectById(id);

// Traverse all descendants
scene.traverse((child) => {
  if (child.isMesh) {
    child.material.wireframe = true;
  }
});

Controls

OrbitControls

import { OrbitControls } from "three/addons/controls/OrbitControls.js";

const controls = new OrbitControls(camera, renderer.domElement);

controls.enableDamping = true;
controls.dampingFactor = 0.05;
controls.minDistance = 2;
controls.maxDistance = 50;
controls.maxPolarAngle = Math.PI / 2;

// Update in animation loop
function animate() {
  controls.update();
  renderer.render(scene, camera);
}

Control Options

controls.enabled = true;
controls.target.set(0, 0, 0);
controls.enableZoom = true;
controls.enableRotate = true;
controls.enablePan = true;
controls.autoRotate = true;
controls.autoRotateSpeed = 2.0;

Other Controls

import { FlyControls } from "three/addons/controls/FlyControls.js";
import { FirstPersonControls } from "three/addons/controls/FirstPersonControls.js";
import { PointerLockControls } from "three/addons/controls/PointerLockControls.js";
import { TrackballControls } from "three/addons/controls/TrackballControls.js";

Raycasting

Mouse Picking

const raycaster = new THREE.Raycaster();
const pointer = new THREE.Vector2();

window.addEventListener("click", (event) => {
  pointer.x = (event.clientX / window.innerWidth) * 2 - 1;
  pointer.y = -(event.clientY / window.innerHeight) * 2 + 1;

  raycaster.setFromCamera(pointer, camera);
  const intersects = raycaster.intersectObjects(scene.children);

  if (intersects.length > 0) {
    const obj = intersects[0].object;
    const point = intersects[0].point;
    const dist = intersects[0].distance;
    const face = intersects[0].face;
  }
});

Raycaster Methods

raycaster.set(origin, direction);
raycaster.setFromCamera(pointer, camera);

// Intersect specific objects
raycaster.intersectObject(object);
raycaster.intersectObjects(objects, recursive);

Loading Models

GLTFLoader

import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";

const loader = new GLTFLoader();

loader.load("model.glb", (gltf) => {
  scene.add(gltf.scene);

  // Access animations
  const mixer = new THREE.AnimationMixer(gltf.scene);
  gltf.animations.forEach((clip) => {
    mixer.clipAction(clip).play();
  });

  // Update in animation loop
  const clock = new THREE.Clock();
  function animate() {
    mixer.update(clock.getDelta());
  }
});

DRACOLoader

import { DRACOLoader } from "three/addons/loaders/DRACOLoader.js";
import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";

const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath("/draco/");

const gltfLoader = new GLTFLoader();
gltfLoader.setDRACOLoader(dracoLoader);

Enables loading of Draco-compressed models.

Other Loaders

import { OBJLoader } from "three/addons/loaders/OBJLoader.js";
import { FBXLoader } from "three/addons/loaders/FBXLoader.js";
import { ColladaLoader } from "three/addons/loaders/ColladaLoader.js";

Scene

Scene Setup

const scene = new THREE.Scene();

scene.background = new THREE.Color(0x000000);
scene.environment = cubeTexture;

// Fog
scene.fog = new THREE.Fog(0xcccccc, 10, 100);
scene.fog = new THREE.FogExp2(0xcccccc, 0.002);

Scene Methods

scene.add(object);
scene.remove(object);
scene.clear();

scene.getObjectByName("meshName");
scene.getObjectById(id);

scene.traverse((child) => {
  if (child.isMesh) {
    // Process mesh
  }
});

Renderer

WebGLRenderer

const renderer = new THREE.WebGLRenderer({
  canvas: canvasElement,
  antialias: true,
  alpha: true,
});

renderer.setSize(width, height);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setClearColor(0x000000, 1);

renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;

renderer.render(scene, camera);

Renderer Properties

renderer.outputColorSpace = THREE.SRGBColorSpace;
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 1.0;
renderer.physicallyCorrectLights = true;

Responsive Resize

Window Resize Handler

window.addEventListener("resize", () => {
  // Update camera
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();

  // Update renderer
  renderer.setSize(window.innerWidth, window.innerHeight);
  renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
});

Handles window resizing and updates camera aspect ratio and renderer size.

Helpers

Debug Helpers

// Axes (RGB = XYZ)
scene.add(new THREE.AxesHelper(5));

// Grid
scene.add(new THREE.GridHelper(10, 10));

// Camera frustum
scene.add(new THREE.CameraHelper(camera));

// Lights
scene.add(new THREE.DirectionalLightHelper(light, 5));
scene.add(new THREE.PointLightHelper(pointLight, 1));
scene.add(new THREE.SpotLightHelper(spotLight));

// Bounding box
scene.add(new THREE.BoxHelper(mesh, 0xff0000));

Skeleton Helper

const helper = new THREE.SkeletonHelper(skinnedMesh);
scene.add(helper);

Vectors & Math

Vector3

const v = new THREE.Vector3(x, y, z);

v.set(x, y, z);
v.add(otherVector);
v.sub(otherVector);
v.multiply(otherVector);
v.multiplyScalar(scalar);
v.normalize();
v.length();
v.distanceTo(otherVector);
v.dot(otherVector);
v.cross(otherVector);
v.lerp(otherVector, alpha);

Common Math

THREE.MathUtils.degToRad(90);
THREE.MathUtils.radToDeg(Math.PI / 2);
THREE.MathUtils.clamp(value, min, max);
THREE.MathUtils.lerp(start, end, alpha);
THREE.MathUtils.randFloat(low, high);
THREE.MathUtils.randInt(low, high);

Post-Processing

EffectComposer

import { EffectComposer } from "three/addons/postprocessing/EffectComposer.js";
import { RenderPass } from "three/addons/postprocessing/RenderPass.js";
import { UnrealBloomPass } from "three/addons/postprocessing/UnrealBloomPass.js";

const composer = new EffectComposer(renderer);
composer.addPass(new RenderPass(scene, camera));

const bloomPass = new UnrealBloomPass(
  new THREE.Vector2(width, height),
  1.5, // strength
  0.4, // radius
  0.85, // threshold
);
composer.addPass(bloomPass);

// Render with composer
composer.render();

Common Passes

import { GlitchPass } from "three/addons/postprocessing/GlitchPass.js";
import { FilmPass } from "three/addons/postprocessing/FilmPass.js";
import { DotScreenPass } from "three/addons/postprocessing/DotScreenPass.js";
import { SMAAPass } from "three/addons/postprocessing/SMAAPass.js";

Gotchas

⚠️ Color space: Set texture.colorSpace = THREE.SRGBColorSpace for diffuse/color textures, or colors will look washed out. Normal maps and other data textures should stay in linear space.

⚠️ Dispose resources: Call geometry.dispose(), material.dispose(), texture.dispose() when removing objects to prevent memory leaks. Three.js doesn't auto-cleanup.

⚠️ Camera aspect ratio: Always update camera.aspect and call camera.updateProjectionMatrix() after resizing the window.

⚠️ Material needs lights: MeshStandardMaterial, MeshPhongMaterial, and MeshLambertMaterial require lights in the scene. Use MeshBasicMaterial for unlit objects.

⚠️ Controls update: When using OrbitControls with damping enabled, you must call controls.update() in your animation loop.

⚠️ Pixel ratio: Cap devicePixelRatio with Math.min(window.devicePixelRatio, 2) to avoid performance issues on high-DPI displays.

Also see