Simulate Soap Bubbles with Three.js Iridescence
This article explains how to use the iridescence properties in
Three.js to simulate thin-film interference, the optical phenomenon
responsible for the shifting rainbow colors seen on soap bubbles, oil
slicks, and camera lenses. You will learn about the physics behind this
effect, the specific properties of MeshPhysicalMaterial
that control it, and how to implement it in your 3D scenes.
What is Thin-Film Interference?
Thin-film interference occurs when light waves reflect off both the upper and lower boundaries of a microscopic thin film. Because the film is extremely thin—comparable to the wavelength of visible light—the two reflected waves interfere with one another.
Depending on the film’s thickness, the refractive index, and the viewer’s angle of observation, certain light wavelengths (colors) amplify each other (constructive interference) while others cancel each other out (destructive interference). As the viewing angle or film thickness changes, the perceived color shifts, creating the classic iridescent rainbow effect.
Implementing Iridescence in Three.js
Three.js simulates this complex wave-theory physics using the
MeshPhysicalMaterial class. To create an
iridescent effect like a soap bubble, you must configure several
specific properties:
1. iridescence
This property controls the intensity of the iridescence effect. It is
a float value, typically ranging from 0.0 (no iridescence)
to 1.0 (maximum iridescence).
2. iridescenceIOR
This represents the Index of Refraction (IOR) of the thin-film layer
itself. It determines how sharply light bends when entering the film,
which alters the interference wavelengths. Common values range between
1.2 and 1.7 (water/soap is roughly
1.33).
3.
iridescenceThicknessRange
This is an array of two numbers, [minimum, maximum],
defining the thickness of the thin film in nanometers (nm). Visible
light waves range from roughly 380 nm to 750 nm. Altering these limits
changes which colors are constructive or destructive, thereby shifting
the color palette of the gradient. A standard range for a soap bubble is
[100, 400] or [100, 800].
4.
iridescenceThicknessMap
To prevent the colors from looking static and uniform, you can supply
a grayscale texture to this property. The texture’s pixel values (0 to
1) map to the minimum and maximum values defined in
iridescenceThicknessRange. This variation simulates natural
thickness differences in a soap bubble, creating a swirling, organic
color pattern.
Code Example: Creating a Soap Bubble
To make a realistic soap bubble, you must combine iridescence with high transmission (transparency) and low roughness.
import * as THREE from 'three';
// Create the geometry
const geometry = new THREE.SphereGeometry(1, 64, 64);
// Create the iridescent physical material
const bubbleMaterial = new THREE.MeshPhysicalMaterial({
roughness: 0.05, // Bubbles are highly reflective and smooth
transmission: 0.95, // High transmission allows light to pass through
ior: 1.1, // Low base IOR to minimize default glass reflection
transparent: true,
opacity: 1.0,
// Iridescence settings
iridescence: 1.0, // Maximize the effect
iridescenceIOR: 1.33, // Soap/water refractive index
iridescenceThicknessRange: [100, 400] // Limits in nanometers for color shifting
});
// Create and add the mesh to the scene
const bubble = new THREE.Mesh(geometry, bubbleMaterial);
scene.add(bubble);How the Shader Calculates the Effect
Under the hood, the Three.js fragment shader calculates the optical
path difference based on: 1. The angle between the camera’s view vector
and the surface normal (the Fresnel effect). 2. The thickness value of
the film at that specific fragment. 3. The specified
iridescenceIOR.
Using these inputs, the renderer dynamically computes an interference color shift and layers it on top of the material’s base diffuse reflections and specular highlights. Consequently, as the camera or the bubble rotates, the colors naturally shift and flow across the surface.