Séance The Exhibition

The Guide · Site 01 of 15

How the séance
was staged

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

03The Signature Technique — Thin-Film Interference

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:

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.

The Séance hero bottle photograph
Séance — the master
The Vestige bottle photograph
Vestige — lilac & silver
The Thurible bottle photograph
Thurible — petrol & gold
The Nocturne Ambre bottle photograph
Nocturne 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.