Three.js Cheatsheet
Lights
Use this Three.js reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.
Light Types Overview
| Light | Shadows | Position | Use Case |
|---|---|---|---|
AmbientLight | No | N/A | Fill light, prevent total black |
HemisphereLight | No | N/A | Sky/ground two-tone ambient |
DirectionalLight | Yes | Direction only (position sets dir) | Sun, moon |
PointLight | Yes | World position | Bulb, torch, lamp |
SpotLight | Yes | World position + target | Flashlight, stage spotlight |
RectAreaLight | No | World position | Soft area light (TV, window) |
AmbientLightandHemisphereLightare cheap — always add one.RectAreaLightrequiresRectAreaLightUniformsLib.init()to work correctly.
AmbientLight
const ambient = new THREE.AmbientLight( 0xffffff, // color 0.4 // intensity (default 1) ); scene.add(ambient);
HemisphereLight
const hemi = new THREE.HemisphereLight( 0x87ceeb, // sky color (light from above) 0x3a2a1a, // ground color (light from below) 0.8 // intensity ); hemi.position.set(0, 20, 0); // position doesn't affect direction, only for helper scene.add(hemi);
DirectionalLight
const dirLight = new THREE.DirectionalLight(0xffffff, 1); dirLight.position.set(5, 10, 7.5); // direction = this position → target.position dirLight.target.position.set(0, 0, 0); // default (world origin) scene.add(dirLight); scene.add(dirLight.target); // target must be in the scene for custom targets // Shadows dirLight.castShadow = true; dirLight.shadow.mapSize.set(2048, 2048); // power of 2; higher = sharper 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; dirLight.shadow.bias = -0.001; dirLight.shadow.normalBias = 0.02; // Debug helper scene.add(new THREE.DirectionalLightHelper(dirLight, 1)); scene.add(new THREE.CameraHelper(dirLight.shadow.camera));
PointLight
const pointLight = new THREE.PointLight( 0xff6600, // color 1, // intensity 10, // distance (0 = infinite attenuation) 2 // decay (physically correct = 2) ); pointLight.position.set(2, 3, 2); scene.add(pointLight); // Shadows pointLight.castShadow = true; pointLight.shadow.mapSize.set(1024, 1024); pointLight.shadow.camera.near = 0.1; pointLight.shadow.camera.far = 20; pointLight.shadow.bias = -0.001; scene.add(new THREE.PointLightHelper(pointLight, 0.3));
SpotLight
const spotLight = new THREE.SpotLight( 0xffffff, // color 1, // intensity 20, // distance (0 = infinite) Math.PI / 6, // angle (half-angle of cone, max PI/2) 0.3, // penumbra (0 = hard edge, 1 = soft) 2 // decay ); spotLight.position.set(0, 10, 0); spotLight.target.position.set(0, 0, 0); scene.add(spotLight); scene.add(spotLight.target); // Shadows spotLight.castShadow = true; spotLight.shadow.mapSize.set(1024, 1024); spotLight.shadow.camera.near = 0.5; spotLight.shadow.camera.far = 30; spotLight.shadow.camera.fov = THREE.MathUtils.radToDeg(spotLight.angle * 2); scene.add(new THREE.SpotLightHelper(spotLight));
RectAreaLight
import { RectAreaLightHelper } from 'three/addons/helpers/RectAreaLightHelper.js'; import { RectAreaLightUniformsLib } from 'three/addons/lights/RectAreaLightUniformsLib.js'; // REQUIRED — must call once before using RectAreaLight RectAreaLightUniformsLib.init(); const rectLight = new THREE.RectAreaLight( 0xffffff, // color 5, // intensity 4, // width 2 // height ); rectLight.position.set(0, 3, 5); rectLight.lookAt(0, 0, 0); // aim at scene center scene.add(rectLight); scene.add(new RectAreaLightHelper(rectLight)); // Note: only works with MeshStandardMaterial and MeshPhysicalMaterial // No shadow support
Light Properties Reference
| Property | Type | Default | Notes |
|---|---|---|---|
light.color | Color | 0xffffff | Light color |
light.intensity | number | 1 | Linear multiplier; physically based renderer scales by physical units |
light.visible | boolean | true | Toggle light on/off |
light.castShadow | boolean | false | Enable shadow casting |
light.shadow.mapSize | Vector2 | (512,512) | Shadow map resolution |
light.shadow.bias | number | 0 | Fix shadow acne (try -0.001) |
light.shadow.normalBias | number | 0 | Alternative acne fix |
light.shadow.radius | number | 1 | PCF blur radius |
pointLight.distance | number | 0 | Attenuation distance (0 = infinite) |
pointLight.decay | number | 2 | Attenuation curve (2 = physically correct) |
spotLight.angle | number | π/3 | Half cone angle in radians |
spotLight.penumbra | number | 0 | Soft edge amount (0–1) |
Helpers
scene.add(new THREE.AmbientLightProbe()); // IBL probe scene.add(new THREE.DirectionalLightHelper(dirLight, size)); scene.add(new THREE.PointLightHelper(pointLight, sphereSize)); scene.add(new THREE.SpotLightHelper(spotLight)); scene.add(new THREE.HemisphereLightHelper(hemi, size)); scene.add(new THREE.CameraHelper(light.shadow.camera)); // shadow frustum // Update helpers after moving light spotLightHelper.update();
Image-Based Lighting (IBL / Environment)
import { RGBELoader } from 'three/addons/loaders/RGBELoader.js'; const pmremGenerator = new THREE.PMREMGenerator(renderer); pmremGenerator.compileEquirectangularShader(); new RGBELoader().load('/hdri/studio.hdr', (hdrTex) => { const envMap = pmremGenerator.fromEquirectangular(hdrTex).texture; scene.environment = envMap; // applies to all PBR materials scene.background = envMap; // optional: show as background hdrTex.dispose(); pmremGenerator.dispose(); });
Light Probes
import { LightProbeGenerator } from 'three/addons/lights/LightProbeGenerator.js'; const probe = new THREE.LightProbe(); scene.add(probe); // Generate from a cube render target const cubeRenderTarget = new THREE.WebGLCubeRenderTarget(256); const cubeCamera = new THREE.CubeCamera(0.1, 100, cubeRenderTarget); scene.add(cubeCamera); cubeCamera.update(renderer, scene); probe.copy(LightProbeGenerator.fromCubeRenderTarget(renderer, cubeRenderTarget));
Performance Tips
Keep total lights to 3–5 maximum for mobile performance. Each shadow-casting light adds one (or six, for point) extra render passes.
PointLightshadows use a cube map (6 passes) — expensive. PreferSpotLightorDirectionalLightwhen shadows are needed.
AmbientLight+HemisphereLightare free (no per-fragment calculation beyond a simple add). Use them generously.
Use
renderer.shadowMap.autoUpdate = falseand callrenderer.shadowMap.needsUpdate = trueonly when scene content changes, for static scenes.
RectAreaLighthas no shadow support and requires the uniform lib — use only with PBR materials.
intensityis in physical light units (lumens, candela) whenrenderer.useLegacyLights = false(default since r155). Set tofalseexplicitly: old scenes may need intensity values multiplied byMath.PI.