---
date: 2026-5-22
---

<!-- markdownlint-disable MD001 -->

# Lighting

![lighting](https://imgs.search.brave.com/Wt2WuY-N15BGYiaM7WNAazVzxtH6zkUibQH_EvDo_GM/rs:fit:860:0:0:0/g:ce/aHR0cHM6Ly9ibG9n/LmxvZ3JvY2tldC5j/b20vd3AtY29udGVu/dC91cGxvYWRzLzIw/MjAvMTEvdGhyZWVq/c19saWdodF9yZW5k/ZXJlcl8zLWUxNjA0/OTUwODM2NzE3LnBu/Zw)

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:

```javascript
// 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.

```javascript
// 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:

```javascript
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.

```javascript
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.

```javascript
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.

```javascript
// 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:

```javascript
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:

```javascript
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:

```javascript
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 value** — `0xffaa44` 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."
