Listen up. If you're building modern applications, understanding Lighting in Three.js 3D WebGL is non-negotiable. This is where simple logic turns into intelligent behavior.
1Why Lighting Matters in Three.js
Welcome to Lighting! If you use a MeshStandardMaterial without any lights, your object will be pitch black. Let's fix that.
MeshStandardMaterial and MeshPhysicalMaterial are physically-based materials ā they compute the final pixel color from the angle and intensity of incoming light using a BRDF (bidirectional reflectance distribution function), so with zero lights in the scene there's simply nothing for that math to work with. This is different from MeshBasicMaterial, which ignores lighting entirely and renders its color flatly regardless of the scene.
Three.js ships several light types ā Ambient, Directional, Point, Spot, Hemisphere, and RectArea ā each modeling a different real-world light source. Picking the right combination is what turns a flat gray blob into something that reads as a genuine 3D object with volume and shading.
// Let there be light3D Scene rendered. Objects: 4, Draw Calls: Optimized.
2AmbientLight: Uniform Global Illumination
The most basic light is AmbientLight. It globally illuminates all objects in the scene equally from all directions. It does not cast shadows.
Because AmbientLight has no position or direction, it can't produce shading, highlights, or shadows ā it simply adds a flat amount of color to every surface, acting as a crude stand-in for indirect bounce light that would otherwise be missing from a real-time scene. Used alone it makes objects look flat and washed out, which is why it's almost always combined with a directional or point light for actual shape definition.
A low intensity (0.2ā0.5) is typical ā just enough to keep shadowed areas from going completely black, without erasing the contrast created by your other lights.
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5); // color, intensity
scene.add(ambientLight);3D Scene rendered. Objects: 4, Draw Calls: Optimized.
3DirectionalLight: Simulating the Sun
Next is the DirectionalLight. Think of it like the Sun. It's a light source located infinitely far away, shining in a specific direction.
Unlike a PointLight, a DirectionalLight's rays are treated as parallel ā every object in the scene is lit from the same angle regardless of how far it is from the light's position, exactly like sunlight reaching Earth. This also means intensity doesn't attenuate with distance, so moving a DirectionalLight further away has no visual effect on brightness, only its rotation (or the vector from its position to its target) matters.
DirectionalLight is the standard choice for outdoor scenes and is also the light type most commonly paired with shadow mapping, since a single consistent ray direction produces stable, predictable shadow shapes.
const dirLight = new THREE.DirectionalLight(0xffffff, 1);
dirLight.position.set(5, 10, 5);
scene.add(dirLight);3D Scene rendered. Objects: 4, Draw Calls: Optimized.
4Adding Lights in React Three Fiber
In React Three Fiber, we use the <ambientLight> and <directionalLight> components.
R3F's lowercase JSX tags map directly onto their THREE.js constructors ā <directionalLight> becomes new THREE.DirectionalLight() under the hood, with each prop (color, intensity, position) forwarded to the corresponding property or constructor argument. This means everything you learned about vanilla Three.js lighting behavior ā falloff, shadow casting, no-direction-for-Ambient ā applies unchanged; only the syntax for creating and configuring the light differs.
Because lights are just regular scene children in R3F, they can be nested inside groups, conditionally rendered, or given refs like any other component, which makes dynamic lighting setups (day/night toggles, colored accent lights) straightforward to build declaratively.
<Canvas>
<ambientLight intensity={0.2} />
<directionalLight color="white" position={[0, 5, 5]} intensity={1} />
{/* Meshes */}
</Canvas>3D Scene rendered. Objects: 4, Draw Calls: Optimized.
5DirectionalLight in Action: Hard-Edged Shading
Let's see DirectionalLight in action! Watch how the light hits one side of the sphere while the other stays completely dark.
This example uses two DirectionalLights ā one red from the right, one blue from the left ā with no AmbientLight to fill in the gaps. On a material with roughness 0.2 and metalness 0.8, that combination produces a sharp, high-contrast falloff between lit and unlit hemispheres, plus tight specular highlights, since low roughness keeps reflections crisp instead of scattering them.
The completely dark side isn't a bug ā it's exactly what happens physically when no light rays reach a surface and there's no ambient or bounce light to compensate. Adding even a faint AmbientLight would soften that transition considerably.
const App = () => {
return (
<Canvas camera={{ position: [0, 0, 5] }}>
<directionalLight position={[5, 0, 0]} intensity={3} color="red" />
<directionalLight position={[-5, 0, 0]} intensity={3} color="blue" />
<mesh>
<sphereGeometry args={[1.5, 64, 64]} />
<meshStandardMaterial roughness={0.2} metalness={0.8} />
</mesh>
</Canvas>
);
};
render(<App />);3D Scene rendered. Objects: 4, Draw Calls: Optimized.
6PointLight: Omnidirectional Point Source
Now let's look at PointLight. Think of it like a lightbulb. It emits light in all directions from a specific point in space.
The constructor's third argument, distance, defines the range past which the light's intensity is treated as zero ā set it to 0 (the default) for physically-accurate inverse-square falloff over infinite range, or a finite value to hard-cap how far the light reaches, which is cheaper to compute and useful for small local effects like a torch or a glowing prop.
PointLights are the natural choice for lightbulbs, candles, explosions, and any light source that genuinely radiates outward from a single point, but they're also the most expensive of the common light types to use with shadows, since Three.js has to render the shadow map six times (once per cube face) to cover all directions.
const pointLight = new THREE.PointLight(0xff0000, 1, 100); // color, intensity, distance
pointLight.position.set(0, 2, 0);3D Scene rendered. Objects: 4, Draw Calls: Optimized.
7Animating Lights with useFrame
We can animate a PointLight to move around the scene! Watch the blue light orbit the sphere.
The useFrame hook runs on every rendered frame, and here it directly mutates lightRef.current.position.x and .z using sin/cos of the elapsed clock time ā a classic parametric circle. Mutating the position directly like this, instead of storing it in React state and re-rendering, is the correct pattern for Three.js animation: state updates and re-renders are far too slow for 60fps object movement.
Because the PointLight moves but the sphere's material stays roughness 0.1 and metalness 0.5, you get a shifting specular highlight that visibly slides across the surface as the light orbits ā a good visual gut-check that your light is actually animating rather than just changing color.
const AnimatedLight = () => {
const lightRef = React.useRef();
useFrame((state) => {
lightRef.current.position.x = Math.sin(state.clock.elapsedTime) * 3;
lightRef.current.position.z = Math.cos(state.clock.elapsedTime) * 3;
});
return <pointLight ref={lightRef} color="#00F0FF" intensity={50} distance={10} />;
};
const App = () => {
return (
<Canvas camera={{ position: [0, 2, 5] }}>
<ambientLight intensity={0.1} />
<AnimatedLight />
<mesh>
<sphereGeometry args={[1, 64, 64]} />
<meshStandardMaterial color="#333" roughness={0.1} metalness={0.5} />
</mesh>
</Canvas>
);
};
render(<App />);3D Scene rendered. Objects: 4, Draw Calls: Optimized.
8SpotLight: Cone-Shaped Illumination
Finally, there is the SpotLight. It works exactly like a flashlight or a stage spotlight, emitting a cone of light.
The angle prop controls how wide the cone spreads (in radians, capped at Math.PI/2), while penumbra softens the cone's edge from a hard circle into a gradual fade ā 0 gives a crisp-edged spotlight, values closer to 1 produce a soft, diffused glow. Like PointLight, SpotLight also supports distance and decay for physically-based falloff.
A SpotLight also has a target ā by default it points at the origin, but assigning target.position (or adding a <Object3D> target in R3F) lets you aim it anywhere, which is essential for effects like a flashlight following the camera or a spotlight highlighting a specific object on a stage.
<spotLight position={[0, 5, 0]} angle={Math.PI / 6} penumbra={0.5} intensity={10} />3D Scene rendered. Objects: 4, Draw Calls: Optimized.
9Lighting Recap and What's Next
Fantastic! You've learned how to illuminate your worlds with Ambient, Directional, Point, and Spot lights. But what about shadows? That's next!
Each light type solves a different real-world lighting scenario: AmbientLight for flat fill, DirectionalLight for sunlight, PointLight for bulbs and local sources, and SpotLight for cones and flashlights ā most real scenes combine two or three of these rather than relying on just one.
Notice that none of these lights have cast a shadow yet, even though DirectionalLight and PointLight support it. Shadow casting requires explicitly enabling castShadow/receiveShadow on lights and meshes and configuring a shadow camera, which is exactly what the next lesson covers.
// š” Scene illuminated!3D Scene rendered. Objects: 4, Draw Calls: Optimized.
10Step-by-Step Breakdown
Welcome to Lighting! If you use a MeshStandardMaterial without any lights, your object will be pitch black. Let's fix that.
The most basic light is AmbientLight. It globally illuminates all objects in the scene equally from all directions. It does not cast shadows.
Which light type illuminates all objects equally from everywhere, without a specific source direction?
- āDirectional
- āAmbient
- āPoint
Next is the DirectionalLight. Think of it like the Sun. It's a light source located infinitely far away, shining in a specific direction.
In React Three Fiber, we use the <ambientLight> and <directionalLight> components.
Let's see DirectionalLight in action! Watch how the light hits one side of the sphere while the other stays completely dark.
Now let's look at PointLight. Think of it like a lightbulb. It emits light in all directions from a specific point in space.
Which light type functions most like a lightbulb hanging in a room?
- āambient
- ādirectional
- āpoint
We can animate a PointLight to move around the scene! Watch the blue light orbit the sphere.
Finally, there is the SpotLight. It works exactly like a flashlight or a stage spotlight, emitting a cone of light.
Which property on a SpotLight softens the edge of the light cone (makes it blurry instead of a harsh circle)?
- āblur
- āpenumbra
- āsoftness
Fantastic! You've learned how to illuminate your worlds with Ambient, Directional, Point, and Spot lights. But what about shadows? That's next!
Level Up š
Advanced cheat sheets, SEO tricks, and interview prep for this topic.
Browser Support
Fully supported.
Fully supported.
Fully supported.
Fully supported.
Accessibility (A11y)
1Describe Lit Scenes for Non-Visual Users
A `<canvas>` element is a single opaque bitmap to assistive technology ā no amount of careful lighting setup is perceivable by a screen reader. Provide an aria-label or nearby visually-hidden text summarizing what the lit scene depicts (e.g. "3D product render lit from above with a warm key light"), so the visual effort put into lighting still conveys meaning to non-sighted users.
<canvas aria-label="Rotating 3D model of a running shoe, lit from the front-left" />SEO Implications
- 1
Light Rigs Are Invisible to Crawlers
Search engines cannot see the effect of an AmbientLight/DirectionalLight/PointLight/SpotLight combination ā WebGL canvas output isn't part of the DOM text or accessibility tree crawlers index. Any SEO value on a lighting-heavy 3D page has to come from surrounding indexable text (headings, captions, alt-style descriptions), not from the render itself.
Best Practices
Combine a Few Lights Rather Than Many
Each dynamic light Three.js's standard forward renderer processes adds real per-fragment shading cost, and shadow-casting lights are far more expensive still. Most convincing scenes use just 2-4 lights (e.g. one Ambient/Hemisphere for fill plus one Directional or Point for shape) rather than stacking many lights to compensate for a poorly-placed key light.
Set Light Intensity for Your Renderer's Tone Mapping
Since Three.js r155+ defaults to physically-based lighting units, intensities that looked right in older projects (e.g. `intensity: 1` on a PointLight) can now appear far too dim or blown out. Always check intensity values against your renderer's `toneMapping` and `outputColorSpace` settings rather than copy-pasting values from older tutorials.
Frequent Bugs
A PointLight or SpotLight appears far too dim or completely invisible despite a high intensity value.
Since Three.js switched to physically-based light units by default, PointLight and SpotLight intensity is now expressed in candela, which requires much larger numbers (tens to hundreds) than the old watt-like units for the light to read as visible ā check your Three.js version's lighting docs rather than assuming intensity: 1 is meaningful.
Real-World Examples
Time-of-Day Lighting in a Product Configurator
E-commerce 3D configurators (sneakers, furniture, cars) often let users toggle between 'studio', 'outdoor', and 'night' presets, each swapping the color, intensity, and position of a DirectionalLight plus an AmbientLight fill to change the product's mood without touching geometry or materials.
const presets = {
studio: { dirColor: '#ffffff', dirIntensity: 3, ambient: 0.6 },
night: { dirColor: '#5566ff', dirIntensity: 0.8, ambient: 0.15 },
};
setLightPreset(presets.studio);