Lab.
The premise is simple: a row of glass-like slabs, each rising and falling with a phase offset, so the motion ripples from one end to the other like a slow breath. Every slab shares the same geometry but carries its own color — a lerp from pale blue at the edges to deep navy at the center, shifting frame by frame with the wave.
Transmission shadows
The first real problem was shadows. MeshPhysicalMaterial with transmission > 0 skips Three.js's depth pass entirely, so the slabs cast nothing on the floor. The fix is a customDepthMaterial on each mesh — forcing the renderer to write depth even for transmissive geometry.
mesh.customDepthMaterial = new THREE.MeshDepthMaterial({
depthPacking: THREE.RGBADepthPacking,
});Soft blur
PCFSoftShadowMap gives you jittered taps — usable, but not truly soft. Switching to VSMShadowMap unlocks shadow.radius and shadow.blurSamples as real Gaussian controls. Paired with a low floor envMapIntensity, the shadows finally read clearly against the reflective surface.
Single file
The entire scene — geometry, materials, lights, post-processing, and GUI — lives in one index.html. No build step, no bundler. Open it in a browser and every parameter is live-tunable from the control panel.



