Lighting
Sometimes you'll set up your scene, add a model, and see... nothing. Just a black shape against a black background. You might think something's broken, but the answer is simple: you forgot to add lights.
Without lights, any MeshStandardMaterial or MeshPhysicalMaterial will appear completely black. They need light to reflect off them so our eyes can see them. A MeshBasicMaterial doesn't need light (it's always fully bright), but it also looks flat and unrealistic.
types of lights
Three.js gives you several kinds of lights, each with a different personality:
| Light | What it's like | Shadows? |
|---|---|---|
| AmbientLight | Soft light from everywhere — fills in dark areas | No |
| DirectionalLight | The sun — parallel rays from one direction | Yes |
| PointLight | A lightbulb — shines in all directions from one spot | Yes |
| SpotLight | A flashlight — a cone of light | Yes |
| HemisphereLight | Sky-to-ground gradient — great for outdoor scenes | No |
setting up each light
Here's how to create each one:
// Ambient light — fills dark areas so nothing is completely black
const ambient = new THREE.AmbientLight(0xffffff, 0.4);
scene.add(ambient);
// Directional light — like the sun
const sun = new THREE.DirectionalLight(0xffaa44, 1);
sun.position.set(5, 10, 7); // position it above and to the side
scene.add(sun);
// Point light — like a bare bulb
const point = new THREE.PointLight(0xffffff, 1.5, 20);
point.position.set(2, 5, 3);
point.castShadow = true;
scene.add(point);
// Spot light — like a flashlight
const spot = new THREE.SpotLight(0xffffff, 2);
spot.position.set(3, 6, 4);
spot.angle = 0.3;
spot.penumbra = 0.2;
spot.decay = 1;
spot.distance = 20;
spot.castShadow = true;
spot.target.position.set(0, 0, 0);
scene.add(spot);
scene.add(spot.target);
// Hemisphere light — sky color on top, ground color on bottom
const hemi = new THREE.HemisphereLight(0x87ceeb, 0x3a3a3a, 1);
scene.add(hemi);
which light should you use?
Most scenes start with a combination of ambient + directional. The ambient light makes sure nothing is pitch black, and the directional light gives you nice crisp shadows and a clear light direction.
// Standard starter setup
const ambient = new THREE.AmbientLight(0xffffff, 0.5);
scene.add(ambient);
const sun = new THREE.DirectionalLight(0xffffff, 1);
sun.position.set(5, 10, 5);
sun.castShadow = true;
scene.add(sun);
☝️ tip: If you're using
MeshBasicMaterial, you don't need lights at all — the material shows its full color regardless. But it won't look as realistic.
lighting quirks & gotchas
Here are the weird things that'll trip you up at least once:
1. Shadows won't show until you enable them on the renderer
This is the #1 shadow mistake. You can set castShadow = true on everything — light, mesh, ground — and still see no shadows. You also need to tell the renderer:
renderer.shadowMap.enabled = true; // !!! required for any shadows
renderer.shadowMap.type = THREE.PCFSoftShadowMap; // soft edges (optional)
Without that line, Three.js ignores all shadow settings.
2. MeshBasicMaterial ignores every light
If you set a material to MeshBasicMaterial, it will be fully bright at all times. No amount of lights will make it darker, lighter, or cast shadows on it. It's like a glow-in-the-dark sticker — it doesn't care about the room lighting.
const material = new THREE.MeshBasicMaterial({ color: 0xff0000 });
// This cube will be bright red no matter how many lights you add
Swap to MeshStandardMaterial if you want it to react to lights.
3. Spot lights need their target added to the scene
A SpotLight points at a target object. That target defaults to position (0, 0, 0) — but if you don't add the target to the scene, the light might point at nothing and shine into the void.
const spot = new THREE.SpotLight(0xffffff, 2);
spot.target.position.set(0, 0, 0);
scene.add(spot.target); // !!! don't forget this
scene.add(spot);
4. HemisphereLight position doesn't matter the way you think
A HemisphereLight doesn't come from a specific point — it's a gradient across your entire scene based on direction. Setting its position won't change much. The only thing that matters is the sky color vs ground color.
// Both of these behave identically:
const hemi1 = new THREE.HemisphereLight(0x87ceeb, 0x3a3a3a, 1);
hemi1.position.set(0, 100, 0);
scene.add(hemi1);
const hemi2 = new THREE.HemisphereLight(0x87ceeb, 0x3a3a3a, 1);
hemi2.position.set(-50, 20, 100);
scene.add(hemi2);
5. Tone mapping will change how bright your scene looks
Three.js applies tone mapping to the final image, and it can darken things more than you expect. If your scene looks too dark even with bright lights, check your renderer settings:
renderer.toneMapping = THREE.ACESFilmicToneMapping; // realistic but dark
renderer.toneMappingExposure = 1.0; // increase this if it's too dark
ACESFilmicToneMapping— most realistic, but can crush dark areasReinhardToneMapping— brighter, good for webNoToneMapping— raw values, often looks washed out
If things look too dim, bump up renderer.toneMappingExposure to 1.5 or 2.0 before increasing light intensities.
6. Shadow acne — the dark stripe nightmare
Sometimes you'll see weird dark stripes or patterns on surfaces that have shadows. That's shadow acne — the shadow map fighting with the surface. Fix it with bias:
directionalLight.shadow.bias = -0.001;
// or for spot/point lights:
spotLight.shadow.bias = -0.001;
Start with -0.001 and adjust until the artifacts disappear. Too much bias and shadows will detach from objects (called "peter panning").
7. Use light helpers to debug invisible lights
If you can't figure out where your light is or what it's hitting, add a helper:
import { DirectionalLightHelper } from 'three/examples/jsm/helpers/DirectionalLightHelper.js';
import { PointLightHelper } from 'three/examples/jsm/helpers/PointLightHelper.js';
import { SpotLightHelper } from 'three/examples/jsm/helpers/SpotLightHelper.js';
// Shows a wireframe representation of the light
const helper = new DirectionalLightHelper(sun, 1);
scene.add(helper);
Helpers show you the light's position, direction, and (for spot lights) the cone shape. Remove them when you're done — they're for debugging only.
a few things to keep in mind
- Position matters — a light's position changes how the scene looks dramatically. Move lights around until it feels right.
- You can have multiple lights — use as many as you need, but each one adds performance cost.
- Intensity is 0–1 (usually) —
1is full brightness,0.5is half. - Color is a hex value —
0xffaa44is a warm orange-yellow.0x8888ffis a cool blue. - Point and Spot lights cost more — they're more expensive to render than directional or ambient.
Experiment! Move lights around, change their colors, turn them on and off. Lighting is one of the easiest ways to make a scene go from "okay" to "beautiful."
