Three.js's physical material can't fake gemstone optics — no internal bounces, total internal reflection, or dispersion. A standard PBR diamond just looks like grey glass, which is unsellable for jewellery.
Ring Builder
A real-time 3D diamond-ring configurator in the browser — customise metal, diamonds, and lighting against a photoreal preview, then export marketing stills and 360° MP4s with no render server.
- year
- 2025
- role
- Frontend Developer
- duration
- 4 months
- live
- —
> stack next.js · three.js · react three fiber · typescript · glsl
A browser-based configurator for fine jewellery. Load a GLB ring model and customise it live — metal shade, diamond colour and refraction, HDR lighting, camera angle — against a photoreal preview, then export marketing assets: high-resolution stills and 360° rotation videos encoded to MP4 entirely client-side. The hard part isn't the panel of controls; it's making a diamond actually read as a diamond at interactive frame rates, and getting video out of WebGL with no render backend.
- 01GLB model import with automatic material detection (metal, diamond, engraving)
- 02Live material editing — metal shades, diamond tints, and index of refraction
- 03Photoreal viewport with a custom ray-traced diamond shader and swappable HDR environments
- 04High-resolution still export up to 1920² with ACES tone mapping and bloom
- 05360° product video exported to MP4 in-browser via FFmpeg.wasm
- 06Save and load configurations, plus batch-render multiple presets in one pass
Metal shade, diamond colour, IOR, HDR map, light angles, camera — every control had to update the GPU immediately, without re-walking and rebuilding the whole model hierarchy on each change.
A six-second 360° clip is ~180 frames; holding them all at export resolution runs to hundreds of megabytes, and there was no server to do the encoding.
A fragment shader traces rays through the diamond geometry — up to eight internal bounces with Fresnel reflect/refract, IOR-based colour shift, and chromatic aberration. A three-mesh-bvh acceleration structure culls non-intersecting triangles so it stays real-time instead of testing every face per ray.
Scene state lives in a plain TypeScript configurator class that notifies subscribers on change; a thin React hook binds it to the UI. Setters batch and push only the changed material via typed-array writes with colour caching — no Redux, no full re-render.
Frames render in batches of thirty, write directly into FFmpeg's virtual filesystem, then clear — never accumulating in JS. Peak memory fell roughly 76%, with parallelised temp-file cleanup trimming encode time.
- a1f0c93chore: Next.js + React Three Fiber scaffold
- 5d8b2e4feat: 3D scene, HDR lighting, orbit controls
- c3e7a19feat: custom diamond shader + BVH ray-tracing
- 7f2d6b0feat: configurator state + live material edits
- b9c4e85feat: GLB import + material auto-detect
- 2e6a1f7feat: image + 360° MP4 export via FFmpeg.wasm
- perfperf: stream frames, cache colours, cut memory 76%
- ✓Real-time, photoreal diamond rendering at interactive frame rates
- ✓Stills and 360° MP4 export running entirely client-side — no render backend
- ✓≈76% lower peak export memory after the streaming-and-caching optimisation pass