How the silk
was woven
Site 01 of 25 — an ethereal perfume house, built entirely by hand.
Le Concept
AURELIA sells hours, not scents — dawn, noon, midnight — so the site needed a material that behaves like bottled light: one continuous surface of liquid silk beneath a pearl serif wordmark, the palette held to pearl, champagne gold, deep ink, and a rose–teal shimmer that appears only where light grazes.
The Silk, Technically
The hero is a single PlaneGeometry(1,1,230,156) facing the camera, drawn with three.js 0.160.0 pinned via importmap — the only library on the page. A custom ShaderMaterial does all the work. The vertex shader displaces the plane with three octaves of hand-inlined Ashima simplex noise plus one long directional sine for the drape, then derives normals by finite differences so lighting stays true to the folds. The cursor is a Gaussian dip written into that same height field: touch the page and the silk yields under your finger.
The fragment shader is the signature: thin-film interference, computed the physical way. Optical path 2·n·d·cosθ runs through a cosine per RGB wavelength (650/545/450 nm), with film thickness driven by fold height — so crests and valleys refract different colours. The raw rainbow is remapped onto a rose–teal duotone, weighted by Fresnel, lit by a champagne key light, and dithered against banding. Pixel ratio is capped at 2; rendering pauses via document.hidden and an IntersectionObserver when the hero scrolls away.
Press the silk and a coordinate drops into a four-slot ripple buffer; a rippleH() term folds an expanding, decaying wavefront into the same height field. No separate ripple shader was needed — thin-film interference already colours whatever the height field gives it. A synced DOM ring blooms at the same instant, so the gesture reads before the GPU's next frame lands.
Everything Else, By Hand
Each fragrance card is a 300×380 Canvas 2D loop: radial-gradient blobs orbiting on lissajous paths, one palette per hour, additive compositing for Minuit only. A radial sheen tracks the real cursor across each card via --mx/--my custom properties. The accord pyramid is pure CSS — translucent radial-gradient orbs in mix-blend-mode: screen, breathing on offset timings; clicking a register echoes the hero ripple outward through its own orbs. A two-part custom cursor — snapped dot, lerped ring — dilates gold over anything touchable, fine-pointer only. Magnetic nav links, card tilt, and lagged parallax all lerp inside one shared requestAnimationFrame loop; reveals use IntersectionObserver, and prefers-reduced-motion collapses everything — cursor and ripple included — to one considered static frame.
The Passes
The build was iterated against headless-Chromium screenshots at 1440×900 and 390×844 — including a delayed frame to prove ambient motion reads in a recording, plus scripted scroll/hover/click passes for sections a single screenshot can't reach. Each pass fixed what the screenshots actually showed: a first pass rebuilt the silk's colour ramp after it read as a muddy smear rather than dawn light; a second added the click ripple, the custom cursor, and staggered entrances where sections arrived as inert blocks; a third chased craft — a cursor-tracked sheen, the ripple extended into the accords, a reveal-timing bug that let a still cursor highlight the wrong register, then a final finish: a moiré artifact dissolved out of the silk's weave, the shimmer held strictly to the house palette, and the keyboard given the same touch as the cursor.