GEMx
Brand identity and a real-time WebGL showroom for a gemstone exchange.
GEMx connects independent gem cutters with buyers who never get to hold the stone. The pitch: if the product is light itself, the website has to render light honestly. I built the identity and the interactive marketing site as one system — a logotype drawn from dispersion optics, and a real-time gemstone renderer that runs on a mid-tier phone.
Gem trading runs on physical inspection — color, cut, and "fire" (dispersion, the rainbow flashes as a stone moves). Photography flattens all three, which is why online gem listings look like costume jewelry and price accordingly. GEMx needed buyers to trust a stone they could only see through a screen.
The constraint that shaped everything: the buyers are not on workstations. They are dealers checking listings on phones in markets in Jaipur, Bangkok, and Antwerp. Path-traced gem renders were off the table — the fire had to be real-time, on hardware I did not control, on networks I could not assume.
A brand drawn from optics
The identity starts where the product does: refraction. The logotype is set on a baseline that kinks at the angle light bends entering corundum (the sapphire family) — a detail nobody consciously reads and every gemologist feels. The palette is sampled from certification-grade color standards rather than a brand-book mood, so a "GEMx emerald green" is a measurable thing, not a vibe.
Everything ships as tokens: the print deck, the site, and the renderer's material presets draw from one palette definition, so the stone on screen and the wordmark beside it agree.
Faking fire honestly
Real dispersion needs spectral ray tracing; phones need something else. The renderer fakes it in layers that each stay cheap: cubemap refraction with a per-channel index of refraction offset (three texture taps buy the rainbow), a facet-space glint pass driven by the device gyroscope so the stone flashes as the phone tilts, and screen-space sparkle seeded per-facet so highlights sit on geometry, not on the screen.
The honesty rule: every approximation was reviewed against reference footage of the actual stones with the gemologist. If a shortcut made a stone look better than it is, it came out — the renderer is a listing, not an ad.
// three refractions, one per channel — the cheap rainbow
vec3 dispersed;
dispersed.r = texture(uEnv, refract(V, N, uIOR * 0.985)).r;
dispersed.g = texture(uEnv, refract(V, N, uIOR )).g;
dispersed.b = texture(uEnv, refract(V, N, uIOR * 1.015)).b;
// facet glint — gyro-driven, seeded per facet so
// sparkle sticks to geometry, not to the screen
float glint = facetSparkle(vFacetId, uTilt); A budget, not a benchmark
The site holds a hard envelope: 60fps on a three-year-old mid-tier Android, first paint before the 3D loads, total transfer including geometry under 3.5MB. A device probe on first frame sorts hardware into three quality tiers — resolution scale, sparkle density, and refraction taps degrade together, so a low tier looks simpler, never broken.
The stone geometry ships as Draco-compressed buffers behind a poster frame; the page is fully readable before WebGL wakes up, and stays fully readable if it never does.
A showroom the founders can run
New stones enter through Sanity: the founders upload certification data and studio turntable photos, and the pipeline derives material presets (IOR, dispersion strength, body color) from the certificate fields. No developer in the loop per listing — the renderer reads the CMS like a database of physics, which is what a gem certificate is.
Dummy numbers, real shape: the interactive showroom became the sales deck — founders open listings in meetings instead of slides. Session data moved the way a showroom should: people stay, tilt the stone, and come back with the phone held sideways.
- 60fps
- on mid-tier mobile three-year-old Android, tier 2
- 1.8s
- first contentful paint page readable before WebGL wakes
- 3.2MB
- total transfer with 3D Draco geometry + KTX2 environments
- +34%
- median session duration vs. the photo-grid predecessor