We open-sourced a scroll-driven WebGL piece that traces a single natural diamond from its birth about 150 km down in the mantle, across three billion years, up to a finished ring. As you scroll, the stone is cut in front of you, from a rough octahedral crystal into a round brilliant.
Live: https://www.prodiam.co.za/oldest/
Source (MIT): https://github.com/prodiamadmin/deep-time-diamond
Here is how it is built.
Stack
- Three.js r184 for the diamond and all 3D
- GSAP + ScrollTrigger so every animation is driven by scroll position
- Lenis for inertial smooth-scroll that the timeline reads from
- HTML5 audio for the voiceover and ambient bed
The diamond is procedural, not a model
There is no .glb. The brilliant is generated from rings of vertices: a table, a table ring, girdle top and bottom, and one culet point at the bottom, stitched into facets. Flat shading means each facet catches light on its own, like a real cut stone:
function makeBrilliant(N) {
const rg = 1.0, rt = 0.56, ht = 0.36, gd = 0.05, pd = 0.92;
const v = [0, ht, 0]; // table center
for (let k = 0; k < N; k++) { // table ring
const a = k / N * Math.PI * 2;
v.push(Math.cos(a) * rt, ht, Math.sin(a) * rt);
}
// ...girdle top, girdle bottom...
v.push(0, -(gd + pd), 0); // culet
// index facets, computeVertexNormals(), flatShading: true
}
The cut is a single morph target
The rough raw crystal is a second set of vertex positions on the same geometry, an irregular octahedron (apex, a jittered wide equator, apex). It starts at full influence, so the very first thing you see is a raw stone:
geo.morphAttributes.position = [new THREE.Float32BufferAttribute(roughPos, 3)];
diamond.morphTargetInfluences = [1]; // 1 = fully rough
During the cutting-bench scene, one GSAP tween drives that influence to 0 and turns the optics on at the same instant. That single tween is the entire rough-to-brilliant reveal:
tl.to(diamond.morphTargetInfluences, { 0: 0, duration: w, ease: 'power2.inOut' }, benchStart)
.to(material, {
roughness: 0, transmission: 1, dispersion: 1.25,
clearcoat: 1, thickness: 2, envMapIntensity: 2.2,
duration: w, ease: 'power2.inOut'
}, benchStart);
Fire, without a path tracer
The material is a MeshPhysicalMaterial with ior: 2.42, the real refractive index of diamond, plus transmission and dispersion for the rainbow fire. Reflections come from a PMREM-prefiltered RoomEnvironment, and the canvas is transparent (alpha: true) so the 3D floats over DOM era-art and film. Tone mapping is ACES Filmic. It holds 60fps on a phone and still reads as a diamond.
Scroll is the timeline
One GSAP master timeline owns everything: camera dolly, rotation, era cross-fades, copy, and the cut. ScrollTrigger scrubs it, and Lenis feeds scroll into the GSAP ticker so the scrub stays smooth:
const lenis = new Lenis({ lerp: 0.08, smoothWheel: !reduced });
lenis.on('scroll', ScrollTrigger.update);
gsap.ticker.add((t) => lenis.raf(t * 1000));
Each act gets a weight, not a fixed height. The deep-time montage (older than the pyramids, older than the first flower) gets about three times the scroll room of any other beat so it actually lands:
const WEIGHTS = [1.1, 3.6, 1.4, 1.4, 1.3, 1.7, 1.5];
scrollEl.style.height = WEIGHTS.reduce((a, b) => a + b, 0) * 100 + 'vh';
Performance and accessibility
- Only the active scene's
<video>plays; the rest are paused as you scroll past. -
devicePixelRatiois capped at 2. -
prefers-reduced-motiondrops the smooth-scroll and the scrub, degrading to plain page scroll. - A
?staticflag exposeswindow.__shot(progress)to render any single frame on demand, which made deterministic QA screenshots trivial.
Why
ProDiam is a natural-diamond cutting house in Bedfordview, South Africa (https://www.prodiam.co.za). They buy rough, cut it in-house, and sell the finished stone, so we wanted to show the part nobody usually sees: the three billion years before the ring.
It is MIT-licensed. Fork it, tear out the diamond, and point the same scroll engine at your own story. Feedback welcome, especially on the dispersion look and the scroll performance.
Top comments (0)