Types of Lighting

Understanding every light source Three.js offers and when to use each one

Why Lighting Matters

Without light, a 3D scene is a black void. Every material in Three.js, from MeshBasicMaterial to MeshPhysicalMaterial, relies on lights to determine what the viewer actually sees. The type, position, color, and intensity of your lights define the mood, depth, and realism of your scene more than almost any other factor.

Three.js provides six built-in light types. Each one models a different real-world lighting scenario, and each comes with its own performance profile and use cases. This article walks through all of them so you can choose the right tool for each situation.

AmbientLight

AmbientLight applies a flat, uniform color to every surface in the scene, regardless of position or orientation. It has no direction and casts no shadows. Think of it as simulating the indirect light that bounces around a room. The light that fills in the dark areas so nothing is completely black.

The demo above shows two spheres lit only by ambient light. Notice how there's no sense of depth. There is no bright side, no dark side. Every face receives the same amount of light. This is exactly why ambient light should never be your only light source. It exists to fill in shadows, not to define them.

typescript
// AmbientLight: uniform light in all directions
const ambient = new THREE.AmbientLight(
  0x404060,  // color, usually dim, cool-toned
  0.5        // intensity, keep it low
);
scene.add(ambient);

// Common mistake: cranking ambient intensity to 1.0+
// This washes out your scene and kills all contrast.
// Use it as a subtle fill, not a primary source.

When to use it: Almost always, as a subtle fill light. Pair it with a directional or point light to prevent pitch-black shadows. Keep intensity low, 0.2 to 0.6 is typical.

Performance: Negligible cost. AmbientLight adds almost nothing to your render time since it applies a single flat color without any per-pixel direction calculations.

DirectionalLight

DirectionalLight emits parallel rays in a single direction, as if the light source is infinitely far away. This is how the sun works. The rays are effectively parallel by the time they reach objects on the ground. The light's position defines the direction it shines from (toward the origin by default), not where it physically sits.

The two spheres above are lit by a single directional light coming from the upper right. Notice the crisp division between the lit and unlit sides, this is characteristic of directional light. Every object receives light from the same angle, regardless of where it is in the scene.

typescript
// DirectionalLight: parallel rays from a direction
const dirLight = new THREE.DirectionalLight(
  0xffffff,  // color, white sunlight
  1.5        // intensity
);

// Position defines the direction, not the location.
// This light shines FROM (5, 10, 5) TOWARD (0, 0, 0).
dirLight.position.set(5, 10, 5);
scene.add(dirLight);

// To change the target (default is origin):
dirLight.target.position.set(2, 0, 0);
scene.add(dirLight.target);

When to use it: Outdoor scenes, any situation where you need sun-like illumination. It's the most common primary light in Three.js scenes. Also useful for UI/product shots where you want even, predictable lighting.

Performance: Low cost per light. Shadow mapping with directional lights is the most common shadow technique, the shadow camera covers a rectangular area, which you'll need to configure carefully for large scenes to avoid blurry or clipped shadows.

PointLight

PointLight emits light in all directions from a single point in space, like a bare light bulb or a candle. Light intensity falls off with distance, objects closer to the point are brighter, and objects far away receive little or no light. This falloff is controlled by the distance and decay parameters.

In the demo, a point light orbits around the spheres. Watch how each sphere brightens as the light approaches and dims as it recedes. The side facing the light is bright; the opposite side falls into shadow. This radial falloff is what gives point lights their natural, organic feel.

typescript
// PointLight: omnidirectional from a point
const pointLight = new THREE.PointLight(
  0xff9944,  // color, warm incandescent
  2.0,       // intensity
  50,        // distance, max range (0 = infinite)
  2          // decay, how fast light fades (2 = physically correct)
);
pointLight.position.set(3, 4, 2);
scene.add(pointLight);

// decay: 2 is physically accurate (inverse-square law)
// decay: 1 is linear falloff (less realistic, easier to control)
// decay: 0 means no falloff (light reaches everywhere equally)

When to use it: Indoor scenes, light bulbs, candles, torches, magic effects, explosions. It's for any localized light source that radiates outward. Multiple point lights create rich, layered indoor environments.

Performance: Moderate cost. Each point light adds a per-pixel calculation for every fragment it reaches. Shadow mapping for point lights is expensive because the GPU must render a cube map (six shadow passes instead of one). If you need many point lights, consider disabling shadows on most of them.

SpotLight

SpotLight emits a cone of light from a point in a specific direction, like a flashlight or a stage spotlight. It combines the position-based nature of a point light with directional focus. You control the cone's width with angle and the softness of the cone's edge with penumbra.

The spotlight above shines down at an angle, creating a visible cone of light. The floor plane shows the circular pool of light with soft edges. Adjusting penumbra from 0 (hard edge) to 1 (fully soft edge) dramatically changes the feel. Use hard-edged for dramatic stage lighting and soft for natural indoor fixtures.

typescript
// SpotLight: focused cone of light
const spotLight = new THREE.SpotLight(
  0xffffff,  // color
  3.0,       // intensity
  30,        // distance, max range
  Math.PI / 6,  // angle, cone half-angle (30° here)
  0.5,       // penumbra, 0 = hard edge, 1 = fully soft
  2          // decay, physically correct falloff
);
spotLight.position.set(0, 8, 0);
scene.add(spotLight);

// The spotlight points at its target (default: origin)
spotLight.target.position.set(0, 0, 0);
scene.add(spotLight.target);

// You can attach the target to a moving object:
// spotLight.target = myCharacter;

When to use it: Flashlights, desk lamps, stage lighting, streetlights, any focused beam. Spotlights are the most visually expressive light in that they create clear pools of light and shadow that draw the viewer's eye.

Performance: Similar cost to point lights for the light calculation itself. Shadow maps are a single pass (unlike point lights) since the light only covers a cone, so spotlight shadows are cheaper than point light shadows. The penumbra softness is computed in the shader and adds minimal cost.

HemisphereLight

HemisphereLight simulates the broad, two-tone illumination of an outdoor sky. It defines a sky color (shining down) and a ground color (shining up), and surfaces receive a blend of the two based on their orientation. A surface facing straight up gets full sky color; a surface facing straight down gets full ground color; surfaces at angles get a smooth mix.

The demo shows spheres lit by a hemisphere light with a blue sky color and a warm brown ground color. The tops of the spheres are cool blue-white, while the undersides pick up the warm ground tone. This two-tone gradient is immediately more naturalistic than flat ambient light.

typescript
// HemisphereLight: sky + ground ambient
const hemiLight = new THREE.HemisphereLight(
  0x87ceeb,  // sky color, light blue
  0x654321,  // ground color, brown earth
  1.0        // intensity
);
scene.add(hemiLight);

// Common outdoor setup: hemisphere for fill + directional for sun
const sun = new THREE.DirectionalLight(0xffeedd, 1.5);
sun.position.set(5, 10, 3);
scene.add(sun);

// This combo gives you natural outdoor lighting with
// almost no setup cost.

When to use it: Replace AmbientLight in outdoor scenes. The sky/ground gradient is a massive visual upgrade over flat ambient for zero additional performance cost. It's the fastest way to make an outdoor scene look natural.

Performance: Same as AmbientLight, it's damned near free. It's a per-vertex calculation with no shadow mapping. Use it liberally.

RectAreaLight

RectAreaLight emits light uniformly across a rectangular plane, simulating the behavior of fluorescent panels, windows, or LED screens. Unlike other lights, it produces soft, physically-based illumination with natural falloff across its surface area. The larger the rectangle, the softer the shadows on nearby objects.

There's an important caveat: RectAreaLight only works with MeshStandardMaterial and MeshPhysicalMaterial. It doesn't cast shadows natively. You'd need a workaround for that, and you must import the RectAreaLightUniformsLib helper to initialize the required shader uniforms.

The panel of light above casts a soft, broad wash across the spheres. Notice how the illumination wraps around the curved surfaces differently from a point or directional light, the rectangular shape of the source is visible in the reflections and the way light grades off at the edges.

typescript
import { RectAreaLightUniformsLib } from 'three/examples/jsm/lights/RectAreaLightUniformsLib.js';

// MUST be called once before using RectAreaLight
RectAreaLightUniformsLib.init();

const rectLight = new THREE.RectAreaLight(
  0xffffff,  // color
  5.0,       // intensity
  4,         // width
  2          // height
);
rectLight.position.set(0, 5, 0);
rectLight.lookAt(0, 0, 0);  // Aim the panel
scene.add(rectLight);

// Optional: visualize the light panel
import { RectAreaLightHelper } from 'three/examples/jsm/helpers/RectAreaLightHelper.js';
scene.add(new RectAreaLightHelper(rectLight));

When to use it: Architectural visualization, interior design scenes, product shots, any scene with panel lighting or large light sources like windows. It's the most physically accurate light type for soft, area-based illumination.

Performance: The most expensive light type. The shader calculations are significantly heavier than point or directional lights. It doesn't support shadows natively, which paradoxically makes it cheaper in that regard, but the illumination computation itself is costly. Use sparingly. One or two per scene is reasonable, not a dozen.

Choosing the Right Light

In practice, most scenes use a combination of two or three light types. Here's a quick reference for common setups:

  • Simple product viewer: DirectionalLight + AmbientLight. Clean, even, predictable.
  • Outdoor environment: DirectionalLight (sun) + HemisphereLight (sky/ground fill). Natural and cheap.
  • Indoor room: Multiple PointLights + AmbientLight. Place them where real fixtures would be.
  • Dramatic/cinematic: SpotLights for key lighting + dim AmbientLight for fill. High contrast, focused attention.
  • Architectural interior: RectAreaLights for panel fixtures + PointLights for accent.

Performance Summary

Not all lights are equal in computational cost. Here's how they rank from cheapest to most expensive:

  1. AmbientLight / HemisphereLight: Nearly free. No per-pixel direction calculations.
  2. DirectionalLight: Low cost. Parallel rays simplify the math. Shadow maps are a single 2D pass.
  3. SpotLight: Moderate. Per-pixel cone math plus single-pass shadow maps.
  4. PointLight: Moderate to high. Omnidirectional, and shadow maps require six passes (cube map).
  5. RectAreaLight: Highest. Complex area-light shader math. No native shadow support.

The general rule: every shadow-casting light is far more expensive than a non-shadow-casting one. If your scene has ten point lights, enable shadows on the one or two that matter most and leave the rest shadow-free. The visual difference is rarely worth the GPU cost of shadowing every light.

Light Helpers

When positioning lights, it's hard to tell exactly where they're pointing or how wide their cone is. Three.js includes helper objects that visualize each light's position, direction, and shape. These are invaluable during development, so add them while building your scene, then remove them before shipping.

typescript
// Helpers for debugging light placement
import { RectAreaLightHelper } from 'three/examples/jsm/helpers/RectAreaLightHelper.js';

// DirectionalLight helper, shows direction arrow
const dirHelper = new THREE.DirectionalLightHelper(dirLight, 1);
scene.add(dirHelper);

// PointLight helper, shows a wireframe sphere
const pointHelper = new THREE.PointLightHelper(pointLight, 0.5);
scene.add(pointHelper);

// SpotLight helper, shows the cone
const spotHelper = new THREE.SpotLightHelper(spotLight);
scene.add(spotHelper);

// HemisphereLight helper, shows sky/ground orientation
const hemiHelper = new THREE.HemisphereLightHelper(hemiLight, 1);
scene.add(hemiHelper);

// RectAreaLight helper, shows the panel
const rectHelper = new RectAreaLightHelper(rectLight);
scene.add(rectHelper);

// Remember to call helper.update() if you move the light
// after creating the helper.

The scene below shows a directional light, a point light, and a spotlight all rendered with their helpers enabled. The yellow lines show the directional light's direction, the wireframe sphere marks the point light, and the cone outlines the spotlight's coverage area.

←   Advanced Mapping ConceptsTypes of Shadows   →