TL;DR
Three.js is the standard JavaScript library for 3D on the web. Create a Scene, add a Camera and Renderer, then populate it with meshes (geometry + material), lights, and textures. Use requestAnimationFrame for the render loop, OrbitControls for camera interaction, GLTFLoader for 3D models, and React Three Fiber for declarative React integration. This guide covers every fundamental with practical code examples.
Key Takeaways
- The core trio is Scene + Camera + Renderer β everything builds on top of these three objects.
- Materials range from unlit (MeshBasicMaterial) to full PBR (MeshPhysicalMaterial) depending on your realism needs.
- The animation loop via requestAnimationFrame drives all motion and interaction in Three.js.
- GLTFLoader with DRACOLoader is the industry-standard pipeline for loading 3D models.
- React Three Fiber (R3F) lets you build Three.js scenes with declarative JSX and React lifecycle hooks.
- Performance hinges on draw call reduction, geometry reuse, InstancedMesh, and proper disposal.
1. What Is Three.js?
Three.js is an open-source JavaScript library that abstracts WebGL into a friendly, high-level API. Instead of writing hundreds of lines of GLSL shader code and managing GPU buffers manually, you work with familiar concepts like scenes, meshes, lights, and cameras. It was created by Ricardo Cabello (Mr.doob) in 2010 and is now the most widely used 3D library on the web, powering product configurators, data visualizations, interactive portfolios, browser games, and immersive experiences.
2. Installation and Setup
Install Three.js from npm and set up a minimal project with Vite for fast development.
Install via npm
# Create a new Vite project
npm create vite@latest my-3d-app -- --template vanilla-ts
cd my-3d-app
# Install Three.js
npm install three
npm install -D @types/three
# Start dev server
npm run devMinimal HTML Setup
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<style>
body { margin: 0; overflow: hidden; }
canvas { display: block; }
</style>
</head>
<body>
<script type="module" src="/src/main.ts"></script>
</body>
</html>3. Scene, Camera, and Renderer Fundamentals
Every Three.js application starts with three core objects: a Scene (the container), a Camera (the viewpoint), and a Renderer (the output). This is the foundation that everything else builds upon.
import * as THREE from 'three';
// 1. Scene β the container for all 3D objects
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x111111);
// 2. Camera β PerspectiveCamera(fov, aspect, near, far)
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
camera.position.set(0, 2, 5);
// 3. Renderer β renders the scene from the camera POV
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
document.body.appendChild(renderer.domElement);
// Render a single frame
renderer.render(scene, camera);Camera Types
| Camera | Use Case | Key Parameters |
|---|---|---|
| PerspectiveCamera | Most 3D scenes β mimics human eye | fov, aspect, near, far |
| OrthographicCamera | 2D-looking / isometric views, CAD | left, right, top, bottom, near, far |
4. Geometries: Box, Sphere, Plane, and Custom
Geometries define the shape of your 3D objects. Three.js provides many built-in geometries, and you can also build custom geometry from vertex data.
Built-in Geometries
// BoxGeometry(width, height, depth, widthSeg, heightSeg, depthSeg)
const box = new THREE.BoxGeometry(1, 1, 1);
// SphereGeometry(radius, widthSegments, heightSegments)
const sphere = new THREE.SphereGeometry(1, 32, 32);
// PlaneGeometry(width, height, widthSeg, heightSeg)
const plane = new THREE.PlaneGeometry(10, 10);
// Other common geometries
const cylinder = new THREE.CylinderGeometry(0.5, 0.5, 2, 32);
const torus = new THREE.TorusGeometry(1, 0.4, 16, 100);
const cone = new THREE.ConeGeometry(0.5, 1.5, 32);
const ring = new THREE.RingGeometry(0.5, 1, 32);Custom BufferGeometry
const geometry = new THREE.BufferGeometry();
// Define a triangle with 3 vertices (x, y, z)
const vertices = new Float32Array([
-1.0, -1.0, 0.0, // vertex 0
1.0, -1.0, 0.0, // vertex 1
0.0, 1.0, 0.0, // vertex 2
]);
geometry.setAttribute(
'position',
new THREE.BufferAttribute(vertices, 3)
);
// Compute normals for lighting
geometry.computeVertexNormals();
const mesh = new THREE.Mesh(
geometry,
new THREE.MeshStandardMaterial({ color: 0x00ff88 })
);
scene.add(mesh);5. Materials: Basic, Standard, Physical, and Shader
Materials define how surfaces look. They control color, shininess, transparency, and how light interacts with the mesh. Choosing the right material balances visual quality against performance.
| Material | Lighting | Cost | Best For |
|---|---|---|---|
| MeshBasicMaterial | None (unlit) | Cheapest | Wireframes, debug, flat UI |
| MeshLambertMaterial | Diffuse only | Low | Non-shiny objects, low-poly |
| MeshPhongMaterial | Diffuse + specular | Medium | Shiny objects, plastic, metal |
| MeshStandardMaterial | PBR | Higher | Realistic scenes (recommended) |
| MeshPhysicalMaterial | PBR + advanced | Highest | Glass, clearcoat, subsurface |
| ShaderMaterial | Custom GLSL | Varies | Custom effects, procedural |
// MeshBasicMaterial β ignores lights
const basic = new THREE.MeshBasicMaterial({
color: 0xff6600,
wireframe: true,
});
// MeshStandardMaterial β PBR with roughness & metalness
const standard = new THREE.MeshStandardMaterial({
color: 0x2194ce,
roughness: 0.4,
metalness: 0.6,
});
// MeshPhysicalMaterial β clearcoat glass effect
const glass = new THREE.MeshPhysicalMaterial({
color: 0xffffff,
transmission: 1.0, // fully transparent like glass
thickness: 0.5,
roughness: 0.05,
ior: 1.5, // index of refraction
});Custom ShaderMaterial
const shaderMat = new THREE.ShaderMaterial({
uniforms: {
uTime: { value: 0.0 },
uColor: { value: new THREE.Color(0x00ffaa) },
},
vertexShader: 'varying vec2 vUv;\n' +
'void main(){vUv=uv;\n' +
'gl_Position=projectionMatrix*modelViewMatrix*vec4(position,1.0);}',
fragmentShader: 'uniform float uTime;uniform vec3 uColor;\n' +
'varying vec2 vUv;\n' +
'void main(){float w=sin(vUv.x*10.0+uTime)*0.5+0.5;\n' +
'gl_FragColor=vec4(uColor*w,1.0);}',
});6. Lighting: Ambient, Directional, Point, and Spot
Lights determine how materials appear. Without lights, PBR materials render black. Combine multiple light types for realistic illumination.
// AmbientLight β uniform, non-directional fill
const ambient = new THREE.AmbientLight(0xffffff, 0.3);
scene.add(ambient);
// DirectionalLight β sunlight, parallel rays
const dirLight = new THREE.DirectionalLight(0xffffff, 1.0);
dirLight.position.set(5, 10, 5);
scene.add(dirLight);
// PointLight β emits in all directions from a point
const pointLight = new THREE.PointLight(0xff4400, 2, 50);
pointLight.position.set(-3, 4, 2);
scene.add(pointLight);
// SpotLight β cone of light with angle and penumbra
const spotLight = new THREE.SpotLight(0xffffff, 2);
spotLight.position.set(0, 8, 0);
spotLight.angle = Math.PI / 6;
spotLight.penumbra = 0.3;
spotLight.target.position.set(0, 0, 0);
scene.add(spotLight);
scene.add(spotLight.target);
// HemisphereLight β sky color + ground color
const hemi = new THREE.HemisphereLight(0x87ceeb, 0x362d1b, 0.5);
scene.add(hemi);7. Textures and Texture Loading
Textures map images onto geometry surfaces. Three.js supports color maps, normal maps, roughness maps, displacement maps, environment maps, and more.
const textureLoader = new THREE.TextureLoader();
// Basic color texture
const colorMap = textureLoader.load('/textures/brick_color.jpg');
const normalMap = textureLoader.load('/textures/brick_normal.jpg');
const roughMap = textureLoader.load('/textures/brick_rough.jpg');
// Apply multiple maps for realistic PBR
const brickMat = new THREE.MeshStandardMaterial({
map: colorMap,
normalMap: normalMap,
roughnessMap: roughMap,
roughness: 1.0,
});
// Repeat and wrap textures
colorMap.wrapS = THREE.RepeatWrapping;
colorMap.wrapT = THREE.RepeatWrapping;
colorMap.repeat.set(4, 4);
// Environment map for reflections
const cubeLoader = new THREE.CubeTextureLoader();
const envMap = cubeLoader.load([
'px.jpg', 'nx.jpg',
'py.jpg', 'ny.jpg',
'pz.jpg', 'nz.jpg',
]);
scene.environment = envMap;8. Animation Loop (requestAnimationFrame)
Three.js does not animate on its own. You create a render loop using requestAnimationFrame that updates objects and renders a new frame every ~16ms (60fps). Use a Clock for frame-rate-independent animation.
const clock = new THREE.Clock();
function animate() {
requestAnimationFrame(animate);
const elapsed = clock.getElapsedTime();
const delta = clock.getDelta();
// Rotate the cube
cube.rotation.x += delta * 0.5;
cube.rotation.y += delta * 0.8;
// Oscillate position
sphere.position.y = Math.sin(elapsed * 2) * 1.5;
// Update shader uniform
shaderMat.uniforms.uTime.value = elapsed;
renderer.render(scene, camera);
}
animate();9. OrbitControls: Interactive Camera
OrbitControls lets users rotate, zoom, and pan the camera with mouse or touch input. It is the most commonly used camera controller in Three.js projects.
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
const controls = new OrbitControls(camera, renderer.domElement);
// Smooth damping for natural feel
controls.enableDamping = true;
controls.dampingFactor = 0.05;
// Restrict zoom range
controls.minDistance = 2;
controls.maxDistance = 20;
// Restrict vertical rotation
controls.maxPolarAngle = Math.PI / 2; // don't go below ground
// Auto-rotate
controls.autoRotate = true;
controls.autoRotateSpeed = 2.0;
// IMPORTANT: update controls in the animation loop
function animate() {
requestAnimationFrame(animate);
controls.update(); // required when damping is enabled
renderer.render(scene, camera);
}
animate();10. Loading 3D Models (GLTF / GLB)
GLTF (GL Transmission Format) is the recommended format for 3D models on the web. GLB is the binary variant that bundles textures into a single file. Use GLTFLoader and optionally DRACOLoader for compressed models.
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js';
// Optional: DRACO compression for smaller files
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath('/draco/');
const gltfLoader = new GLTFLoader();
gltfLoader.setDRACOLoader(dracoLoader);
// Load a model
gltfLoader.load(
'/models/robot.glb',
(gltf) => {
const model = gltf.scene;
model.scale.set(0.5, 0.5, 0.5);
model.position.set(0, 0, 0);
scene.add(model);
// Play animations if present
if (gltf.animations.length > 0) {
const mixer = new THREE.AnimationMixer(model);
const action = mixer.clipAction(gltf.animations[0]);
action.play();
// Update mixer in animation loop:
// mixer.update(delta);
}
},
(progress) => {
const pct = (progress.loaded / progress.total) * 100;
console.log(pct.toFixed(1) + '% loaded');
},
(error) => {
console.error('Failed to load model:', error);
}
);11. Shadows
Shadows add depth and realism. They require four settings: enable on the renderer, enable castShadow on the light, enable castShadow on meshes, and enable receiveShadow on surfaces.
// 1. Enable shadows on the renderer
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
// 2. Configure shadow-casting light
dirLight.castShadow = true;
dirLight.shadow.mapSize.width = 2048;
dirLight.shadow.mapSize.height = 2048;
dirLight.shadow.camera.near = 0.5;
dirLight.shadow.camera.far = 50;
dirLight.shadow.camera.left = -10;
dirLight.shadow.camera.right = 10;
dirLight.shadow.camera.top = 10;
dirLight.shadow.camera.bottom = -10;
// 3. Objects that cast shadows
cube.castShadow = true;
sphere.castShadow = true;
// 4. Ground plane receives shadows
const ground = new THREE.Mesh(
new THREE.PlaneGeometry(20, 20),
new THREE.MeshStandardMaterial({ color: 0x808080 })
);
ground.rotation.x = -Math.PI / 2;
ground.receiveShadow = true;
scene.add(ground);12. Raycasting and Interaction
Raycasting lets you detect which objects the user clicks or hovers over. It casts an invisible ray from the camera through the mouse position and checks for intersections with 3D objects.
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();
window.addEventListener('click', (event) => {
// Convert screen coordinates to normalized device coords
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
// Test against specific objects
const intersects = raycaster.intersectObjects(
[cube, sphere, torus]
);
if (intersects.length > 0) {
const hit = intersects[0];
console.log('Clicked:', hit.object.name);
console.log('Distance:', hit.distance.toFixed(2));
console.log('Point:', hit.point);
// Visual feedback β change color
hit.object.material.color.set(0xff0000);
}
});13. Responsive Canvas
A proper Three.js app must handle window resizing and high-DPI displays. Without this, the canvas will look stretched or blurry on different screens.
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
});14. Post-Processing Effects
Post-processing applies screen-space effects after the scene is rendered. Three.js provides an EffectComposer pipeline where you chain passes like bloom, depth-of-field, and color grading.
import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js';
import { RenderPass } from 'three/addons/postprocessing/RenderPass.js';
import { UnrealBloomPass } from 'three/addons/postprocessing/UnrealBloomPass.js';
import { OutputPass } from 'three/addons/postprocessing/OutputPass.js';
// Create composer
const composer = new EffectComposer(renderer);
// Pass 1: render the scene normally
composer.addPass(new RenderPass(scene, camera));
// Pass 2: bloom glow effect
const bloomPass = new UnrealBloomPass(
new THREE.Vector2(window.innerWidth, window.innerHeight),
0.8, // strength
0.3, // radius
0.85 // threshold
);
composer.addPass(bloomPass);
// Pass 3: output (tone mapping + color space)
composer.addPass(new OutputPass());
// Replace renderer.render with composer.render
function animate() {
requestAnimationFrame(animate);
controls.update();
composer.render(); // instead of renderer.render(scene, camera)
}
animate();15. React Three Fiber (R3F)
React Three Fiber is a React renderer for Three.js. It lets you write Three.js scenes as declarative JSX components. R3F manages the canvas, animation loop, disposal, and events automatically.
Installation
npm install three @react-three/fiber @react-three/drei
npm install -D @types/threeBasic R3F Scene
import { Canvas, useFrame } from '@react-three/fiber';
import { OrbitControls } from '@react-three/drei';
import { useRef } from 'react';
import * as THREE from 'three';
function SpinningBox() {
const meshRef = useRef<THREE.Mesh>(null);
useFrame((state, delta) => {
if (meshRef.current) {
meshRef.current.rotation.x += delta * 0.5;
meshRef.current.rotation.y += delta * 0.8;
}
});
return (
<mesh ref={meshRef}>
<boxGeometry args={[1, 1, 1]} />
<meshStandardMaterial color="#2194ce" />
</mesh>
);
}
export default function App() {
return (
<Canvas camera={{ position: [0, 2, 5], fov: 75 }}>
<ambientLight intensity={0.3} />
<directionalLight position={[5, 10, 5]} />
<SpinningBox />
<OrbitControls enableDamping />
</Canvas>
);
}Loading Models in R3F
import { useGLTF } from '@react-three/drei';
function Robot() {
const { scene } = useGLTF('/models/robot.glb');
return <primitive object={scene} scale={0.5} />;
}
// Preload for instant display
useGLTF.preload('/models/robot.glb');16. Performance Optimization
3D rendering is GPU-intensive. Optimizing draw calls, geometry, and resource management is critical for smooth 60fps experiences, especially on mobile devices.
InstancedMesh for Repeated Objects
// Instead of 1000 separate Mesh objects (1000 draw calls),
// use InstancedMesh (1 draw call)
const count = 1000;
const geometry = new THREE.BoxGeometry(0.2, 0.2, 0.2);
const material = new THREE.MeshStandardMaterial();
const instances = new THREE.InstancedMesh(
geometry, material, count
);
const dummy = new THREE.Object3D();
for (let i = 0; i < count; i++) {
dummy.position.set(
(Math.random() - 0.5) * 20,
(Math.random() - 0.5) * 20,
(Math.random() - 0.5) * 20
);
dummy.rotation.set(
Math.random() * Math.PI,
Math.random() * Math.PI,
0
);
dummy.updateMatrix();
instances.setMatrixAt(i, dummy.matrix);
}
instances.instanceMatrix.needsUpdate = true;
scene.add(instances);Key Optimization Strategies
| Strategy | Impact | How |
|---|---|---|
| Reuse geometries and materials | High | Share the same Geometry/Material across meshes instead of creating new ones |
| InstancedMesh | Very High | Render thousands of identical objects in a single draw call |
| Merge geometry | High | Use BufferGeometryUtils.mergeGeometries for static objects |
| LOD (Level of Detail) | Medium | Show lower-poly models when objects are far from camera |
| Limit shadow maps | Medium | Use 1024 or 2048 max; disable shadows on unimportant objects |
| Cap pixel ratio | High | Math.min(window.devicePixelRatio, 2) prevents 3x or 4x rendering |
| Dispose resources | Critical | Call geometry.dispose(), material.dispose(), texture.dispose() when removing objects |
Proper Disposal Pattern
function dispose(obj) {
if (obj.geometry) obj.geometry.dispose();
const mats = Array.isArray(obj.material)
? obj.material : [obj.material];
mats.forEach((m) => {
if (m.map) m.map.dispose();
if (m.normalMap) m.normalMap.dispose();
m.dispose();
});
}
scene.remove(oldMesh);
dispose(oldMesh);17. Debugging Tips
Debugging 3D scenes can be challenging. Here are essential tools and techniques to diagnose issues quickly.
Helpers
// AxesHelper β shows X (red), Y (green), Z (blue) axes
scene.add(new THREE.AxesHelper(5));
// GridHelper β ground grid for spatial reference
scene.add(new THREE.GridHelper(20, 20));
// Light helpers β visualize light position/direction
scene.add(new THREE.DirectionalLightHelper(dirLight, 2));
scene.add(new THREE.PointLightHelper(pointLight, 0.5));
scene.add(new THREE.SpotLightHelper(spotLight));
// Camera helper β visualize shadow camera frustum
scene.add(
new THREE.CameraHelper(dirLight.shadow.camera)
);
// Box3Helper β visualize bounding boxes
const box3 = new THREE.Box3().setFromObject(model);
scene.add(new THREE.Box3Helper(box3, 0xff0000));Performance Monitoring
// Stats.js β live FPS, MS, MB monitor
import Stats from 'three/addons/libs/stats.module.js';
const stats = new Stats();
document.body.appendChild(stats.dom);
function animate() {
stats.begin();
requestAnimationFrame(animate);
controls.update();
renderer.render(scene, camera);
stats.end();
}
// Renderer info β check draw calls and triangles
console.log('Draw calls:', renderer.info.render.calls);
console.log('Triangles:', renderer.info.render.triangles);
console.log('Geometries:', renderer.info.memory.geometries);
console.log('Textures:', renderer.info.memory.textures);Common Issues and Solutions
| Problem | Cause | Fix |
|---|---|---|
| Black screen | No lights in scene or camera facing wrong direction | Add AmbientLight, check camera.position and camera.lookAt |
| Object not visible | Object is outside camera frustum or scale is too small/large | Check position, scale, near/far planes, use AxesHelper |
| Shadow acne (stripy shadows) | Shadow bias too low | Set light.shadow.bias = -0.001 |
| Blurry textures | Texture filtering or low resolution | Set texture.minFilter = THREE.LinearFilter, use higher res images |
| Memory leak | Geometries/materials/textures not disposed | Call .dispose() on every removed resource |
| Low FPS | Too many draw calls or large shadow maps | Use InstancedMesh, merge geometry, reduce shadowMap size |
18. Complete Example: Putting It All Together
Here is a complete, production-ready Three.js setup combining everything from this guide: scene, camera, renderer, lighting, materials, animation, controls, shadows, responsiveness, and debugging helpers.
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x1a1a2e);
const camera = new THREE.PerspectiveCamera(
60, innerWidth / innerHeight, 0.1, 100
);
camera.position.set(3, 3, 5);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(innerWidth, innerHeight);
renderer.shadowMap.enabled = true;
document.body.appendChild(renderer.domElement);
scene.add(new THREE.AmbientLight(0xffffff, 0.2));
const sun = new THREE.DirectionalLight(0xffd580, 1.5);
sun.position.set(5, 8, 3);
sun.castShadow = true;
scene.add(sun);
const ground = new THREE.Mesh(
new THREE.PlaneGeometry(30, 30),
new THREE.MeshStandardMaterial({ color: 0x333355 })
);
ground.rotation.x = -Math.PI / 2;
ground.receiveShadow = true;
scene.add(ground);
const cube = new THREE.Mesh(
new THREE.BoxGeometry(1, 1, 1),
new THREE.MeshStandardMaterial({ color: 0x2194ce })
);
cube.position.y = 0.5;
cube.castShadow = true;
scene.add(cube);
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
addEventListener('resize', () => {
camera.aspect = innerWidth / innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(innerWidth, innerHeight);
});
const clock = new THREE.Clock();
(function animate() {
requestAnimationFrame(animate);
cube.rotation.y = clock.getElapsedTime() * 0.5;
controls.update();
renderer.render(scene, camera);
})();Frequently Asked Questions
What is Three.js and why should I use it?
Three.js is a JavaScript library that makes WebGL easy. It provides a high-level API for creating 3D scenes, cameras, lights, materials, and animations in the browser without writing raw GLSL shaders. It powers thousands of production sites including product configurators, data visualizations, games, and immersive experiences.
How do I install Three.js in a project?
Install via npm: npm install three. Then import with: import * as THREE from "three". For quick prototyping you can also use a CDN script tag or importmap. TypeScript types are included in the package since r150.
What is the difference between MeshBasicMaterial and MeshStandardMaterial?
MeshBasicMaterial ignores lights entirely and renders a flat color, making it cheap but unrealistic. MeshStandardMaterial uses physically-based rendering (PBR) with roughness and metalness parameters and reacts to lights, producing realistic results at a higher GPU cost.
How do I load a GLTF or GLB 3D model in Three.js?
Import GLTFLoader from "three/addons/loaders/GLTFLoader.js". Create a loader instance and call loader.load(url, callback). The callback receives a gltf object whose gltf.scene property is a Group you add to your scene. Use DRACOLoader for compressed models.
How do I make a Three.js canvas responsive?
Listen for the window resize event. In the handler, update camera.aspect to window.innerWidth / window.innerHeight, call camera.updateProjectionMatrix(), and call renderer.setSize(window.innerWidth, window.innerHeight). Also set renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)) to handle high-DPI displays without excessive GPU load.
What is React Three Fiber and how does it relate to Three.js?
React Three Fiber (R3F) is a React renderer for Three.js. It lets you build Three.js scenes using JSX components like <mesh>, <boxGeometry>, and <meshStandardMaterial>. It handles the animation loop, disposal, and lifecycle automatically while giving you full access to the Three.js API via hooks like useFrame and useThree.
How do I enable shadows in Three.js?
Set renderer.shadowMap.enabled = true, set light.castShadow = true on your light source, set mesh.castShadow = true on objects that cast shadows, and set mesh.receiveShadow = true on surfaces that receive shadows. Configure shadow map resolution with light.shadow.mapSize for quality.
How do I optimize Three.js performance?
Key optimizations include: reuse geometries and materials across meshes, use InstancedMesh for repeated objects, reduce draw calls by merging geometry with BufferGeometryUtils.mergeGeometries, limit shadow map resolution, use LOD (Level of Detail) for distant objects, dispose unused resources, and cap pixel ratio at 2.
Conclusion
Three.js gives you the power to create stunning 3D experiences directly in the browser. Start with the fundamentals β Scene, Camera, Renderer β then layer on materials, lights, and textures. Use OrbitControls for camera interaction, GLTFLoader for 3D models, and the requestAnimationFrame loop for animation. When building with React, adopt React Three Fiber for a declarative workflow. Focus on performance from the start by reusing resources, leveraging InstancedMesh, and properly disposing unused objects. The web is your canvas β now build something amazing.