Concept, palette, one shader worth explaining, and an
honest diary. Designed and built end-to-end by Claude Fable 5 — brief to deploy,
no human hands on the keyboard.
01The Concept
Séance Parfums is an haute parfumerie that claims to bottle the residue of
dreams. The creative direction is occult-adjacent luxury — Ouija-board elegance,
never Halloween. The reference points are engraved apothecary labels, table-rapping
parlours of the 1880s, and the way oil on black water carries every colour
without owning any of them.
The entire site is lit by ONE overwhelming visual: a full-viewport GLSL fluid in a
thin-film interference palette, churning slowly beneath every section. It is not a
background: it is a lighting rig. Scrolling from the hero into the Ritual dims it
like a gas lamp being turned down; it flares again for the Collection and goes
almost dark for the Ledger. Nothing on this site cuts — everything condenses,
the way smoke settles into a shape.
The copy is written restrained: no perfume-industry slop, no "unleash your senses."
Every scent is an appointment with something that no longer exists.
02Palette & Type
Void#0B0A10 — the room
Ivory#F2EDE3 — the engraving
Lilac#C9B8FF — the presence
Oil-slickcomputed, not picked — see 03
Italiana — ABCDEFGHIJKLM NOPQRSTUVWXYZ 1234567890
Display · Italiana 400 · engraved-plate capitals, tracked wide
Cormorant Garamond carries the body — and the italics carry
the voice. Set at 300–500 against near-black, it reads like a label that has
been held up to candlelight: quick to whisper, slow to shout.
Body · Cormorant Garamond variable 300–700 + italic
The hero fluid's palette is not a gradient. It is the physics of an oil film:
when light hits a film a few hundred nanometres thick, each wavelength interferes
with its own reflection. Some wavelengths cancel, some reinforce — which is why
petrol on wet asphalt shows green, magenta and gold that move as the film flows.
The shader builds a slowly domain-warped fbm noise field and reads it as film
thickness in nanometres. For each RGB channel we compute the interference
phase directly — 4·π·n·d·cosθ / λ — and reconstruct the reflected colour:
// display.frag.glsl — the interference core
float dnm = 240.0 + 480.0 * f + 230.0 * fen; // film thickness, nm
float cosT = mix(1.0, 0.42, smoothstep(0.0, 1.25, length(p)));
vec3 lambda = vec3(651.0, 532.0, 447.0); // R, G, B in nm
vec3 phase = (12.56637 * 1.38 * dnm * cosT) / lambda; // 4πnd·cosθ/λ
vec3 irid = 0.5 + 0.5 * cos(phase);
irid *= irid; // deepen the bands
Because the colour comes from d (thickness) rather than a lookup, every
disturbance is chromatically honest: thickening the film where the cursor has
passed slides the whole spectrum, exactly as dragging a finger through an oil
slick would. A second fbm field masks the luminance so most of the frame stays
void — a nebula, not a lava lamp.
04Cursor Velocity Becomes Curl
A low-resolution ping-pong buffer (256px tall) carries a disturbance field:
RG is flow velocity, B is energy. The pointer's smoothed velocity is splatted in
each frame — but a straight push would smear. Smoke coils. So alongside the
push, the sim injects a signed vortex around the cursor's path: points to the left
of travel rotate one way, points to the right the other. That sign comes from the
2D cross product of the offset and the direction of motion:
// sim.frag.glsl — curl injection around the cursor path
vec2 nd = uMouseVel / mlen; // direction of travel
float side = d.x * nd.y - d.y * nd.x; // which side of the path?
vec2 perp = vec2(-nd.y, nd.x);
vel += perp * sign(side) * mlen * splat * uDt * 1.5;
The display pass then bends its warp domain by this field
(warp = p + 1.9·(q−0.5) + fvel·2.4), so the slick folds where
a hand has passed and slowly forgets. Hovering the SÉANCE letterforms drives a
third term — a radial pull toward the title's centre — which is the "inhale."
Everything decays exponentially; nothing snaps.
05The Asset Pipeline
Four photographs, one bottle. The hero bottle was generated first
(Higgsfield, nano-banana-pro): a smoked-glass apothecary bottle, brass
stopper, ivory escutcheon label engraved SÉANCE, floating in a black void before
an iridescent ribbon. That master was then re-uploaded as a reference image
for the three collection bottles, with the prompt instructed to change only two
things — the label lettering, and the colour of the iridescent ribbon:
Vestige — "cold and ghostly — pale lilac, silver, faint ice-green;
the stopper now polished pale silver."
Thurible — "deep petrol green and antique gold, one faint wisp of
incense smoke curling past the shoulder."
Nocturne Ambre — "burnt amber, deep magenta and antique gold; the
liquid showing a faint amber warmth."
Reference-image conditioning is what keeps the four bottles reading as one
commission — same glass, same label plate, same rim light. Cost: 8 credits
total (2 per image). Masters stay out of the build; the site ships optimized
WebP at 800/1600px.
Séance — the masterVestige — lilac & silverThurible — petrol & goldNocturne Ambre — amber & magenta
06Iteration Diary
Pass 1 — Structure
The first shader calibration was a full-viewport psychedelic wash — exactly
the lava lamp the brief forbids. Rebalanced until the void owned most of the
frame: a narrower film-thickness range (fewer interference orders), a tighter
luminance mask with low-frequency pools of pure black, and a cross-channel
grade that turns primary green into petrol and warm bands into gold. Also
found and fixed: the hero wordmark breaking into two lines on phones, a
scroll cue whose intro and scroll animations fought over the same property,
and 32px of horizontal overflow caused by a film-grain layer with negative
insets — fixed elements cannot be clipped by their ancestors.
Pass 2 — Craft
Replaced three.js with ~250 lines of raw WebGL — the site only ever needed
two fragment shaders on a fullscreen triangle, and the payload fell from
171KB to 56KB gzipped with pixel-identical output. Sibling reveals now
cascade instead of arriving as one block; the eaux stagger in, the ledger
fills row by row. Added the social card (a true frame of the shader), an
italic-face preload so the hero poem never flashes unstyled, and a
one-line-only rule for the wordmark at every width.
Pass 3 — The Hostile Critic
The correspondence section was the weakest screen: correct, and forgettable.
It gained a seal — a hairline ellipse, the same escutcheon shape engraved on
the bottle labels, that inscribes itself around "Join the Circle" as the
section arrives. The bottles now levitate almost imperceptibly over the
fluid; ledger rows warm and their office glows lilac under the cursor; and
focusing the email field changes its placeholder to "… we are listening"
while the fluid brightens — the page acknowledging that it is being touched.
Every interactive surface now answers back, quietly.
07Colophon
Vite 7 · three.js (two custom GLSL passes) · GSAP + ScrollTrigger · Lenis smooth
scroll · Italiana & Cormorant Garamond, self-hosted · images via Higgsfield
MCP, optimized to WebP · hosted on Cloudflare Pages.
Honours prefers-reduced-motion: the fluid renders as a still, the
ritual unfolds as a static column, and every reveal is disabled.