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

LightShadowsPositionUse Case
AmbientLightNoN/AFill light, prevent total black
HemisphereLightNoN/ASky/ground two-tone ambient
DirectionalLightYesDirection only (position sets dir)Sun, moon
PointLightYesWorld positionBulb, torch, lamp
SpotLightYesWorld position + targetFlashlight, stage spotlight
RectAreaLightNoWorld positionSoft area light (TV, window)

AmbientLight and HemisphereLight are cheap — always add one. RectAreaLight requires RectAreaLightUniformsLib.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

PropertyTypeDefaultNotes
light.colorColor0xffffffLight color
light.intensitynumber1Linear multiplier; physically based renderer scales by physical units
light.visiblebooleantrueToggle light on/off
light.castShadowbooleanfalseEnable shadow casting
light.shadow.mapSizeVector2(512,512)Shadow map resolution
light.shadow.biasnumber0Fix shadow acne (try -0.001)
light.shadow.normalBiasnumber0Alternative acne fix
light.shadow.radiusnumber1PCF blur radius
pointLight.distancenumber0Attenuation distance (0 = infinite)
pointLight.decaynumber2Attenuation curve (2 = physically correct)
spotLight.anglenumberπ/3Half cone angle in radians
spotLight.penumbranumber0Soft 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.

PointLight shadows use a cube map (6 passes) — expensive. Prefer SpotLight or DirectionalLight when shadows are needed.

AmbientLight + HemisphereLight are free (no per-fragment calculation beyond a simple add). Use them generously.

Use renderer.shadowMap.autoUpdate = false and call renderer.shadowMap.needsUpdate = true only when scene content changes, for static scenes.

RectAreaLight has no shadow support and requires the uniform lib — use only with PBR materials.

intensity is in physical light units (lumens, candela) when renderer.useLegacyLights = false (default since r155). Set to false explicitly: old scenes may need intensity values multiplied by Math.PI.