3D Graphics

Three.js

Three.js makes WebGL approachable: a scene graph, cameras, materials and lights let you build 3D experiences for the browser in plain JavaScript.

advanced14 min readUpdated Sep 15, 2026
scene.js
js
// scene.js
import * as THREE from "three";

const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
  75,
  innerWidth / innerHeight,
  0.1,
  100,
);
camera.position.z = 3;

const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);

const cube = new THREE.Mesh(
  new THREE.BoxGeometry(),
  new THREE.MeshStandardMaterial({ color: 0x22c55e }),
);
scene.add(cube);

renderer.setAnimationLoop(() => {
  cube.rotation.y += 0.01;
  renderer.render(scene, camera);
});
Built on
WebGL
Core concept
Scene graph
Renderer
WebGLRenderer
Models
glTF / GLTFLoader
React
React Three Fiber
Cost
GPU and bundle weight

Why it matters

Why 3D on the web

A scene graph

Add objects to a scene and Three.js handles the maths, transforms and draw calls for you.

Hardware accelerated

WebGL draws with the GPU, so smooth 3D is possible on modern devices and phones.

Rich material system

Standard, physical and custom materials plus lights, shadows and post-processing create convincing visuals.

The big picture

The three parts of a Three.js app

A scene holds your objects, a camera decides the view, and a renderer draws it using WebGL.

The scene

Content

A graph of meshes, lights, cameras and groups positioned in 3D space.

The camera

View

Perspective or orthographic, defining what the viewer sees.

The renderer

Draw

WebGLRenderer draws the scene each frame, and animation updates it over time.

Three.js at a glance

The core of Three.js

Geometry and material

A mesh is a geometry (shape) plus a material (surface).

Camera

PerspectiveCamera for realistic depth, Orthographic for flat views.

Lights

Directional, ambient, point and spot lights shape the scene.

Textures and models

Load images and glTF models with loaders.

Raycaster

Detect what the pointer is over for interaction.

Performance

Reuse geometry, limit lights and dispose of resources.

A short history

From raw WebGL to approachable 3D

  1. 2011

    Three.js released

    Ricardo Cabello creates a library to make WebGL accessible.

    11
  2. 2013

    WebGL widespread

    Browser support matures and 3D experiences become common.

    13
  3. 2017

    glTF standard

    A compact 3D format makes loading models practical.

    17
  4. 2020

    React Three Fiber

    A declarative React renderer brings 3D into component code.

    20
  5. Today

    Immersive web

    Product configurators, data visualisation, games and WebXR experiences.

    Today

The complete guide

Three.js: Everything you need to know

What is 3D on the web?

WebGL lets the browser draw hardware-accelerated 3D using the GPU. It is powerful, but the raw API is verbose: you manage buffers, write shaders and handle the maths yourself. Three.js wraps WebGL in a friendly scene graph so you can describe objects, lights and cameras and let the library handle the rest.

The result is that 3D on the web is now practical for product configurators, data visualisation, interactive storytelling, games and immersive WebXR experiences. It is not the right choice for every site, but when motion and space tell the story better than a flat page, Three.js is the standard tool.

Scene, camera, renderer

Every Three.js app has three core objects.

// setup.js
import * as THREE from "three";

const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, innerWidth / innerHeight, 0.1, 100);
camera.position.set(0, 0, 3);

const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(Math.min(devicePixelRatio, 2));
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);

The scene is the container. The camera defines the view; PerspectiveCamera gives realistic depth, while OrthographicCamera is useful for flat or isometric views. The renderer draws the scene to a canvas. Clamping the pixel ratio keeps high-density screens from rendering more pixels than they need.

Meshes, geometry and materials

A visible object is a mesh: a geometry for its shape and a material for its surface.

// mesh.js
const cube = new THREE.Mesh(
  new THREE.BoxGeometry(1, 1, 1),
  new THREE.MeshStandardMaterial({ color: 0x22c55e, roughness: 0.4 }),
);
cube.position.set(0, 0, 0);
scene.add(cube);

Three.js ships many built-in geometries (box, sphere, plane, torus and more) and materials for different looks. MeshStandardMaterial responds to lights and is a good default; MeshBasicMaterial ignores lighting and is cheaper. Meshes can be grouped and nested, forming the scene graph that transforms inherit through.

Lights and shadows

Without light, most materials render black.

// lights.js
const ambient = new THREE.AmbientLight(0xffffff, 0.4);
const key = new THREE.DirectionalLight(0xffffff, 2.5);
key.position.set(2, 3, 2);

scene.add(ambient, key);

Lights are objects in the scene, so their position and type matter. Shadows require enabling renderer.shadowMap and marking the light, the object and the surface, and they are relatively expensive, so use them deliberately. Every light adds cost, so keep the count low.

Models and textures

Real projects usually load assets rather than build geometry by hand. glTF is the standard web 3D format.

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

const loader = new GLTFLoader();
loader.load("/models/robot.glb", (gltf) => {
  scene.add(gltf.scene);
});

Textures load similarly with TextureLoader, and RGBELoader or PMREMGenerator can supply environment maps for realistic reflections. Keep model and texture sizes reasonable, since they dominate load time and memory.

The animation loop

Animation updates the scene and re-renders it each frame.

// loop.js
const clock = new THREE.Clock();

renderer.setAnimationLoop(() => {
  const delta = clock.getDelta();
  cube.rotation.y += delta;
  renderer.render(scene, camera);
});

Using delta keeps motion speed consistent regardless of frame rate. When the scene is static, render on demand instead of continuously: render after a change, a control interaction or an animation, and stop when nothing is happening. That saves battery and GPU on mobile.

Interaction

To make objects clickable or hoverable, use a Raycaster.

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

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

  raycaster.setFromCamera(pointer, camera);
  const hit = raycaster.intersectObjects(scene.children)[0];
  if (hit) hit.object.material.color.set(0xf97316);
});

Raycasting from the camera through the pointer returns the objects under it, which is enough for selection, highlighting and simple interactions.

Performance and cleanup

3D is the most expensive thing most sites will ever render, so discipline matters.

  • Dispose geometries, materials and textures when you remove objects; they live in GPU memory.
  • Reuse geometry and materials across instances instead of creating new ones.
  • Merge static geometry and use instancing for repeated objects.
  • Limit lights and shadow-casting objects.
  • Lower the pixel ratio on high-density displays.
  • Render on demand for static scenes.
  • Pause rendering when the canvas is offscreen.
// dispose.js
mesh.geometry.dispose();
mesh.material.dispose();
scene.remove(mesh);

Forgetting to dispose is the most common cause of a Three.js app that slowly consumes all available memory.

React Three Fiber

With React, React Three Fiber renders Three.js declaratively, so the scene is described with components and integrates with React state and lifecycle.

// Scene.jsx
import { Canvas } from "@react-three/fiber";
import { OrbitControls } from "@react-three/drei";

export function Scene() {
  return (
    <Canvas>
      <ambientLight intensity={0.4} />
      <directionalLight position={[2, 3, 2]} intensity={2.5} />
      <mesh>
        <boxGeometry />
        <meshStandardMaterial color="#22c55e" />
      </mesh>
      <OrbitControls />
    </Canvas>
  );
}

The drei helpers provide controls, environments and common abstractions. It is the recommended way to use Three.js in React.

Best practices

  • Clamp the pixel ratio and resize the renderer with the window.
  • Reuse geometry and materials; instance repeated objects.
  • Dispose of GPU resources when removing objects.
  • Keep lights and shadow casters to a minimum.
  • Render on demand for static scenes.
  • Test on real mid-range mobile devices.
  • Provide a fallback when WebGL is unavailable.

Common mistakes

  • Forgetting to dispose and leaking GPU memory.
  • Rendering continuously when nothing changes.
  • Creating new geometry or materials every frame.
  • Adding many lights and shadows without measuring.
  • Loading huge unoptimised models and textures.
  • Testing only on a fast desktop and shipping to mobile.

Where to go next

Three.js opens a whole dimension for the web. Add choreographed motion with the Advanced Animation guide, build with React Three Fiber if you use React, and keep the cost in check with the Web Performance guide. Then build the smallest possible scene — a lit cube you can orbit — and grow it one concept at a time.

Managing GPU resources

Geometries, materials and textures live in GPU memory. Dispose of them when you remove an object, or the scene leaks.

Prefer
function remove(mesh) {
  mesh.geometry.dispose();
  mesh.material.dispose();
  mesh.material.map?.dispose();
  scene.remove(mesh);
}
Avoid
scene.remove(mesh);
// geometry and textures
// stay in GPU memory

Rendering frames

Render continuously only when the scene changes. For static scenes, render on demand to save battery and GPU.

Prefer
// render when needed
controls.addEventListener("change", () => {
  renderer.render(scene, camera);
});
Avoid
renderer.setAnimationLoop(() => {
  renderer.render(scene, camera);
});
// runs at 60fps forever,
// even when nothing moves

FAQ

Frequently asked questions

Keep learning

Related topics from the roadmap.

$ start learning

Ready to start learning Three.js / WebGL?

Our interactive tutorial walks you through Three.js / WebGL step by step — with quizzes and real code you can run in the browser.