---
title: "Shadows"
type: concept
tags: [threejs, shadows, lighting, shadow-map, intermediate, foundational]
created: 2026-08-02
updated: 2026-08-02
sources: ["raw/web_community-shadows.md"]
confidence: high
threejs_docs_snapshot: "2026-08-02"
---

## Definition

Three.js casts shadows using **shadow maps** by default: for every shadow-casting light, every shadow-casting object in the scene is re-rendered from that light's point of view, and the resulting depth data is used at final-render time to decide which pixels of the scene are in shadow.

## How It Works

Enabling shadows requires switching on three cooperating flags — the renderer, the light, and each mesh:

```js
const renderer = new THREE.WebGLRenderer({antialias: true, canvas});
renderer.shadowMap.enabled = true;

const light = new THREE.DirectionalLight(color, intensity);
light.castShadow = true;

const groundMesh = new THREE.Mesh(planeGeo, planeMat);
groundMesh.receiveShadow = true;   // ground only receives

const cubeMesh = new THREE.Mesh(cubeGeo, cubeMat);
cubeMesh.castShadow = true;        // casts and receives
cubeMesh.receiveShadow = true;
```

**The cost is per light, per shadow-casting object.** With 20 shadow-casting objects and 5 shadow-casting lights, the scene is drawn 6 times per frame — once from each light's viewpoint to build its shadow map, plus once for the actual camera view. A single shadow-casting `PointLight` alone costs 6 extra scene draws, because it shines in all directions and its shadow is effectively 6 `SpotLight` shadows pointed at the faces of a surrounding cube. This is why production scenes commonly limit shadow-casting to one directional light rather than letting every light cast shadows, and why some apps precompute static lighting into lightmaps/ambient-occlusion maps, or fake shadows entirely with a soft round texture on a plane beneath an object instead of a real shadow map.

**Each shadow-casting light type uses a different shadow camera:**

- `DirectionalLight` uses an `OrthographicCamera` whose `left`/`right`/`top`/`bottom`/`near`/`far`/`zoom` you set directly on `light.shadow.camera`. Only what's inside that camera's box receives/casts shadows — visualize it with `new THREE.CameraHelper(light.shadow.camera)`.
- `SpotLight`'s shadow camera is a `PerspectiveCamera` whose `fov` is driven automatically by the light's own `angle`, and whose `aspect` is derived from the shadow map size — most of it is not manually tunable the way a `DirectionalLight`'s is.
- `PointLight`'s shadow camera only exposes `near`/`far` since the light shines in every direction; internally it renders 6 directions like a `SpotLight` shadow per cube face, which is why it is the most expensive of the three.

**Shadow map resolution is a separate control from shadow camera area.** The shadow camera's box is stretched across a fixed-size texture (`light.shadow.mapSize.width`/`.height`, default `512x512`). Making the shadow camera's area larger without increasing map size spreads the same pixel budget over more space, producing blocky, low-resolution shadows — the fix is either a smaller shadow camera box (tightly fit to the scene) or a larger map size (more memory, slower to compute). `renderer.capabilities.maxTextureSize` reports the hardware ceiling for map size.

## Key Parameters

- `renderer.shadowMap.enabled` — master switch; default `false`.
- `light.castShadow` — must be set per light for that light to produce a shadow map.
- `mesh.castShadow` / `mesh.receiveShadow` — set per mesh; a ground plane typically only needs `receiveShadow = true`, while foreground objects typically need both.
- `light.shadow.camera.{left,right,top,bottom,near,far,zoom}` — `DirectionalLight`'s shadow camera frustum; smaller area = sharper shadows for the same map resolution.
- `light.shadow.mapSize.{width,height}` — shadow map texture resolution, default `512x512`; larger costs more memory and compute.
- `light.shadow.camera.{near,far}` — for `SpotLight` and `PointLight`, objects closer than `near` or farther than `far` never receive a shadow from that light.
- `renderer.capabilities.maxTextureSize` — hardware ceiling on shadow map size.

## When To Use

Enable real shadow maps when a scene's depth cues genuinely need cast shadows and the object/light count is small enough to afford the extra per-light render passes — see [[concepts/lighting]] for the renderer-level `shadowMap.autoUpdate` toggle that avoids recomputing shadows every frame once lighting is static. For scenes with many simple objects (characters, props) where a rough contact shadow suffices, a cheap fake shadow (a semi-transparent round texture on a plane, with `depthWrite: false` to avoid z-fighting between overlapping fake shadows) avoids the shadow-map cost entirely — the games *Animal Crossing: Pocket Camp* and *Monument Valley* are cited as using exactly this technique.

## Risks & Pitfalls

- Forgetting any one of the three required flags (`renderer.shadowMap.enabled`, `light.castShadow`, `mesh.castShadow`/`receiveShadow`) silently produces no shadow.
- A `DirectionalLight` shadow camera box that's too small clips shadows at its edges — use `CameraHelper` on `light.shadow.camera` to see the actual coverage area.
- Making the shadow camera box larger to "just cover everything" without raising `mapSize` produces blocky, low-resolution shadows — area and resolution are independent knobs that must be tuned together.
- Enabling shadows on many lights (or any `PointLight`) multiplies per-frame render cost — each shadow-casting light means the whole scene is drawn again from its viewpoint, and a `PointLight` costs 6x that on its own.
- Placing a fake-shadow plane exactly at ground level (instead of a hair above it) can z-fight with the ground; the shadows source uses `y = 0.001` and `depthWrite: false` to avoid this.

## Related Concepts

Shadows are configured on the [[concepts/lighting|lights]] and [[concepts/renderer]] that also govern general illumination, and interact with the [[concepts/cameras|camera]] frustum concepts reused for each light's shadow camera. See also [[concepts/performance]] for the general principle of trading render passes for frame rate, and [[concepts/threejs-overview]].

## Sources

- `raw/web_community-shadows.md` — manual article: shadow-map mechanics and per-light/per-object render-pass cost, the fake-shadow-texture technique, enabling `renderer.shadowMap.enabled`/`light.castShadow`/`mesh.castShadow`/`receiveShadow`, `DirectionalLight`'s `OrthographicCamera` shadow camera and `CameraHelper` visualization, `SpotLight`'s `PerspectiveCamera` shadow camera tied to `angle`, `PointLight`'s 6-direction shadow cost, and the `mapSize` vs shadow-camera-area resolution tradeoff.
