Lighting

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 areas
  • ReinhardToneMapping — brighter, good for web
  • NoToneMapping — 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) — 1 is full brightness, 0.5 is half.
  • Color is a hex value0xffaa44 is a warm orange-yellow. 0x8888ff is 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."