Three.js has been the default answer to "how do I do 3D in the browser" for over a decade. In 2026, it is more capable than ever: WebGPU support is stable, React Three Fiber makes it idiomatic in React apps, and the tooling around GLTF loading and shader composition has matured significantly. This roadmap takes you from a blank canvas to shipping real 3D work.
What changed in 2026
- Three.js r165+ ships a WebGPU renderer alongside the WebGL renderer — switch with a flag and get compute shaders and better GPU utilization on supported browsers.
- React Three Fiber v9 aligned with React 19 — concurrent rendering and Suspense-based asset loading are first-class.
- Drei v10 added new helpers for portals, instanced meshes, and physics integrations, reducing boilerplate significantly.
@react-three/fiber + @react-three/drei + @react-three/rapier is the canonical 2026 stack for interactive 3D in React.
- GLTF 2.0 extensions (KHR_materials_unlit, KHR_animation_pointer) are now broadly supported, improving asset portability from Blender.
Learning roadmap
| Phase |
Topics |
Weeks |
| 1. Core Three.js |
Scene, camera, renderer, geometries, materials |
1–2 |
| 2. Animation |
requestAnimationFrame loop, GSAP, Tween.js |
1 |
| 3. Loading assets |
GLTF/GLB models, textures, draco compression |
1–2 |
| 4. Lighting |
AmbientLight, DirectionalLight, HDRI env maps |
1 |
| 5. Shaders |
ShaderMaterial, GLSL basics, uniforms |
2–3 |
| 6. React Three Fiber |
R3F + Drei, declarative scene |
2 |
| 7. Physics & interaction |
Rapier, raycasting, pointer events |
1–2 |
| 8. WebGPU + performance |
TSL shaders, instancing, draw call budgets |
ongoing |
Phase 1: core Three.js
Every Three.js scene has three parts: a Scene (container), a Camera (viewpoint), and a Renderer (draws to a canvas).
import * as THREE from "three";
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 5;
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// A mesh = geometry + material
const cube = new THREE.Mesh(
new THREE.BoxGeometry(1, 1, 1),
new THREE.MeshStandardMaterial({ color: 0x6366f1 })
);
scene.add(cube);
// Light — MeshStandardMaterial needs light
scene.add(new THREE.DirectionalLight(0xffffff, 2));
function animate() {
requestAnimationFrame(animate);
cube.rotation.y += 0.01;
renderer.render(scene, camera);
}
animate();
Master this loop before adding frameworks. Everything R3F does is this loop, managed for you.
Phase 2: loading 3D assets
Real projects use GLTF/GLB models, not procedural geometries. Use GLTFLoader and compress models with Draco.
import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
import { DRACOLoader } from "three/addons/loaders/DRACOLoader.js";
const draco = new DRACOLoader();
draco.setDecoderPath("/draco/");
const loader = new GLTFLoader();
loader.setDRACOLoader(draco);
loader.load("/models/scene.glb", (gltf) => {
scene.add(gltf.scene);
});
Draco compression reduces GLB file sizes by 60–90%. Always enable it for production assets.
Phase 3: React Three Fiber
R3F maps Three.js classes to JSX elements. Every lowercase-with-tag corresponds to a Three.js constructor.
import { Canvas, useFrame } from "@react-three/fiber";
import { OrbitControls, Environment } from "@react-three/drei";
import { useRef } from "react";
function RotatingCube() {
const ref = useRef<THREE.Mesh>(null);
useFrame((_, delta) => {
if (ref.current) ref.current.rotation.y += delta;
});
return (
<mesh ref={ref}>
<boxGeometry args={[1, 1, 1]} />
<meshStandardMaterial color="#6366f1" />
</mesh>
);
}
export default function App() {
return (
<Canvas camera={{ position: [0, 0, 5] }}>
<ambientLight intensity={0.5} />
<directionalLight position={[5, 5, 5]} />
<RotatingCube />
<OrbitControls />
<Environment preset="city" />
</Canvas>
);
}
useFrame is the R3F animation hook — runs every frame inside the render loop.
Phase 4: shaders
Custom shaders unlock effects that built-in materials cannot achieve: custom noise, procedural textures, post-processing.
// fragment.glsl — simple UV gradient shader
varying vec2 vUv;
uniform float uTime;
void main() {
vec3 color = vec3(vUv.x, vUv.y, sin(uTime) * 0.5 + 0.5);
gl_FragColor = vec4(color, 1.0);
}
const material = new THREE.ShaderMaterial({
vertexShader: /* glsl */`
varying vec2 vUv;
void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.); }
`,
fragmentShader: /* imported from file */,
uniforms: { uTime: { value: 0 } },
});
// Update in animate loop:
material.uniforms.uTime.value = clock.getElapsedTime();
In 2026, Three.js TSL (Three.js Shading Language) provides a JavaScript-based shader DSL for the WebGPU renderer — worth learning if you target WebGPU.
How to start
- Build the spinning cube example without any framework — understand scene/camera/renderer/loop.
- Work through Bruno Simon's "Three.js Journey" — it is the most comprehensive course and updated for 2025.
- Port a simple Three.js scene to R3F — see how the abstraction maps.
- Load a GLTF model from Sketchfab or Poly Pizza (CC0 models).
- Write one custom shader — start with a UV debug shader, then add uniforms.
Common mistakes
Creating new geometries inside the animation loop. Geometry allocation is expensive. Create once, reuse always.
Not disposing resources. geometry.dispose(), material.dispose(), and texture.dispose() must be called when removing objects. Wasm memory does not GC automatically in Three.js.
Too many draw calls. Each mesh.add() to the scene is a draw call. Use InstancedMesh for hundreds of identical objects — the difference can be 100× on GPU.
Ignoring the pixelRatio setting. renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)) — cap at 2, not the raw device value, or retina displays will run at 3× and tank frame rate.
What to skip
- Raw WebGL for learning Three.js — learn it for deeper understanding, but not as a prerequisite.
- A-Frame for interactive 3D apps — A-Frame is great for WebXR declarative scenes, but Three.js/R3F gives more control for non-VR work.
- Babylon.js comparison paralysis — both are capable; Three.js has a larger community and more resources in 2026.
FAQ
Do I need to know 3D modeling to learn Three.js?
No — you can use procedural geometries (boxes, spheres, planes) and free CC0 GLTF models while learning. Modeling skills help later.
How is Three.js performance in 2026?
Excellent with proper instancing and draw call budgeting. WebGPU unlocks compute shaders for GPU-side simulation. Most mobile devices handle simple scenes at 60 fps.
Should I learn R3F or plain Three.js first?
Plain Three.js first. R3F hides a lot; understanding the underlying scene graph makes debugging R3F far easier.
What are good free GLTF model sources?
Poly Pizza (CC0), Sketchfab (filter by CC license), and the official Khronos glTF samples repository.
Where to go next