Modern web browsers are incredibly powerful, possessing direct access to the computer's Graphics Processing Unit (GPU) via the WebGL API. However, writing raw WebGL is notoriously difficult—requiring hundreds of lines of complex matrix mathematics just to draw a colored triangle. Three.js is a library that abstracts this complexity, allowing developers to build complex 3D scenes, lighting models, and animations using an intuitive object-oriented API.
Module 1: The Three Pillars of a 3D World
Every Three.js application requires three core components to render anything to the screen: The Scene, The Camera, and The Renderer.
import * as THREE from 'three';
// 1. The Scene Graph: The container that holds all objects, lights, and cameras.
const scene = new THREE.Scene();
// Optional: Add a background color or fog
scene.background = new THREE.Color(0x111111);
scene.fog = new THREE.FogExp2(0x111111, 0.02);
// 2. The Camera: Defines the perspective from which the user views the scene.
// Params: Field of View (FOV), Aspect Ratio, Near Clipping Plane, Far Clipping Plane
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 2, 10); // Move camera back 10 units and up 2 units
// 3. The Renderer: The WebGL engine that computes the pixels and draws to the HTML <canvas>.
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: false });
renderer.setSize(window.innerWidth, window.innerHeight);
// Enable physically accurate shadows
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
document.body.appendChild(renderer.domElement);Module 2: Meshes (Geometry + Materials)
A 3D object in Three.js is called a Mesh. A Mesh is composed of two things: Geometry (the mathematical skeleton defined by vertices and faces) and a Material (the skin that defines how it reacts to light).
// Create a complex geometry (a knot)
const geometry = new THREE.TorusKnotGeometry(1, 0.3, 100, 16);
// Create a Physically Based Rendering (PBR) material
const material = new THREE.MeshStandardMaterial({
color: 0xff0055,
metalness: 0.8, // Looks like metal
roughness: 0.2, // Slightly shiny
envMapIntensity: 1 // Reflects the environment
});
const torusKnot = new THREE.Mesh(geometry, material);
// Enable shadows for this specific object
torusKnot.castShadow = true;
torusKnot.receiveShadow = true;
scene.add(torusKnot);Module 3: Lighting Architecture
Without light, MeshStandardMaterial objects are entirely black. Lighting defines the mood and realism of your scene.
// Ambient Light: Illuminates all objects equally from all sides. Casts no shadows.
const ambientLight = new THREE.AmbientLight(0xffffff, 0.2);
scene.add(ambientLight);
// Directional Light: Mimics the sun. Parallel rays originating from infinity.
const sunLight = new THREE.DirectionalLight(0xffffff, 1.5);
sunLight.position.set(10, 20, 10);
sunLight.castShadow = true;
// Optimize the shadow map resolution for performance
sunLight.shadow.mapSize.width = 1024;
sunLight.shadow.mapSize.height = 1024;
scene.add(sunLight);
// Point Light: Mimics a lightbulb. Emits light in all directions from a point.
const pointLight = new THREE.PointLight(0x00aaff, 2, 50);
pointLight.position.set(-5, 5, 0);
scene.add(pointLight);Module 4: The Animation Loop & Controls
To make the scene interactive and animated, we create a render loop hooked into the browser's refresh rate.
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
// Add mouse controls to rotate, zoom, and pan the camera
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true; // Adds smooth friction
const clock = new THREE.Clock();
function animate() {
requestAnimationFrame(animate);
const elapsedTime = clock.getElapsedTime();
// Rotate the object smoothly over time
torusKnot.rotation.y = elapsedTime * 0.5;
torusKnot.rotation.x = elapsedTime * 0.2;
// Update controls for damping
controls.update();
// Render the final frame
renderer.render(scene, camera);
}
animate();Module 5: Advanced — Custom GLSL Shaders
Standard materials are pre-compiled by Three.js. If you want completely custom visual effects (like a glowing magical portal, flowing water, or dissolving particles), you must write your own shaders using GLSL (OpenGL Shading Language). Shaders run directly on the GPU in parallel across millions of pixels simultaneously.
const customShaderMaterial = new THREE.ShaderMaterial({
uniforms: {
uTime: { value: 0.0 }, // Passed from the JS animation loop
uColor: { value: new THREE.Color(0x00ffff) }
},
// Vertex Shader: Runs once for every vertex. Used to deform shapes.
vertexShader: `
uniform float uTime;
varying vec2 vUv;
void main() {
vUv = uv;
vec3 newPosition = position;
// Add a wave effect to the geometry based on time and the X coordinate
newPosition.z += sin(newPosition.x * 10.0 + uTime) * 0.1;
gl_Position = projectionMatrix * modelViewMatrix * vec4(newPosition, 1.0);
}
`,
// Fragment Shader: Runs once for every pixel. Used to color the geometry.
fragmentShader: `
uniform float uTime;
uniform vec3 uColor;
varying vec2 vUv;
void main() {
// Create a scrolling neon stripe effect
float strength = sin(vUv.y * 20.0 + uTime * 5.0);
vec3 finalColor = mix(vec3(0.0), uColor, strength);
gl_FragColor = vec4(finalColor, 1.0);
}
`
});