Toggle Lights in Three.js Without Removing Them
Dynamically controlling illumination is essential for creating
interactive 3D environments and optimizing performance in WebGL
applications. This article explains how to toggle the visibility of
specific lights in a Three.js scene without removing them from the scene
graph. You will learn the two primary methods for achieving this—using
the .visible property and manipulating light
.intensity—along with the performance implications of each
approach.
Method 1: Using the
.visible Property
The most straightforward way to turn a light on or off in Three.js is
by toggling its boolean visible property. Inherited from
the Object3D base class, this property determines whether
the object is rendered and processed in the scene.
// Create a point light
const pointLight = new THREE.PointLight(0xffffff, 1, 100);
scene.add(pointLight);
// Turn the light off
pointLight.visible = false;
// Turn the light back on
pointLight.visible = true;
// Toggle the light state dynamically (e.g., on a button click)
function toggleLight() {
pointLight.visible = !pointLight.visible;
}Performance Consideration: Shader Recompilation
While modifying visible is simple, toggling this
property changes the number of active lights in the scene. When the
number of active lights changes, Three.js may need to recompile the
WebGL materials/shaders of objects illuminated by that light. This
recompilation can cause a temporary frame rate drop (a “stutter” or
“jank”) during gameplay or interaction.
Method 2: Setting
.intensity to Zero
To avoid shader recompilation and ensure smooth performance, you can
toggle a light’s visibility by setting its intensity
property to 0.
Because the light technically remains “active” within the scene graph, Three.js does not need to recompile the shaders; it simply calculates the light’s contribution as zero.
// Store the default intensity of your light
const defaultIntensity = 1.5;
const directionalLight = new THREE.DirectionalLight(0xffffff, defaultIntensity);
scene.add(directionalLight);
// Turn the light "off"
directionalLight.intensity = 0;
// Turn the light "on"
directionalLight.intensity = defaultIntensity;
// Toggle the light state dynamically
function toggleLightSmoothly() {
if (directionalLight.intensity > 0) {
directionalLight.intensity = 0;
} else {
directionalLight.intensity = defaultIntensity;
}
}Advantages of the Intensity Method
- No Shader Compilation Stutter: Modifying intensity keeps the shader structure intact, maintaining a stable frame rate.
- Allows for Dimming Effects: You can easily animate
or transition the light on and off using libraries like GSAP, or by
linearly interpolating (
lerp) the intensity value in your animation loop.
Summary: Which Method Should You Use?
- Use
light.visible = falseif you are setting up light states before rendering the scene, if shader compilation lag is not noticeable in your project, or if you want to completely exclude the light from calculations. - Use
light.intensity = 0if you are toggling lights dynamically during runtime (such as flickering lights, day/night cycles, or user-triggered flashlights) to guarantee a stutter-free, 60fps experience.