WebXR Lighting Estimation with Three.js

Integrating real-world lighting into a virtual scene creates highly realistic Augmented Reality (AR) experiences. This article explains how to configure a WebXR session using the WebXR Light Estimation API and apply those real-time lighting values to illuminate a Three.js scene, ensuring your 3D models seamlessly match the brightness, color, and reflections of the physical environment.

1. Request the Light Estimation Feature

To use lighting estimation, you must request the light-estimation feature when initializing your WebXR session. In Three.js, this is configured using the ARButton utility by passing the feature into the optionalFeatures or requiredFeatures array.

import { ARButton } from 'three/addons/webxr/ARButton.js';

const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.xr.enabled = true;

document.body.appendChild(ARButton.createButton(renderer, {
    optionalFeatures: ['light-estimation']
}));

2. Use the Three.js XREstimatedLight Helper

Three.js provides a built-in helper class called XREstimatedLight that handles the complex math of converting WebXR light probe data (spherical harmonics and environment maps) into light sources that Three.js materials can use.

Import the helper and instantiate it by passing your renderer:

import { XREstimatedLight } from 'three/addons/webxr/XREstimatedLight.js';

const xrLight = new XREstimatedLight(renderer);

3. Handle Estimation Events

The XREstimatedLight object dispatches events when the WebXR session starts estimating light. You must listen for these events to add the light sources to your scene and apply the generated environment map for realistic reflections.

xrLight.addEventListener('estimationstart', () => {
    // Add the estimated directional and ambient lights to the scene
    scene.add(xrLight);

    // Apply the estimated environment map to the scene for reflections
    if (xrLight.environment) {
        scene.environment = xrLight.environment;
    }
});

xrLight.addEventListener('estimationend', () => {
    // Remove the lights and reflections if estimation stops
    scene.remove(xrLight);
    scene.environment = null;
});

4. Render the Scene

Once configured, Three.js automatically updates the light’s intensity, color, direction, and environment map on every frame of the XR animation loop. Ensure your materials (such as MeshStandardMaterial or MeshPhysicalMaterial) are configured to receive shadows and reflections to take full advantage of the real-world illumination.