White until refracted.
The page's single job
VERRE is a fictional two-person glass studio on Refshaleøen, Copenhagen, selling hand-blown one-offs to people who buy few, careful things. The page has one job: make you believe that colour here exists only inside glass. So the site is near-white everywhere — and every trace of chroma you meet arrives through a rendered or simulated piece of glass.
Three inks and a hidden field
- Gallery white
#FAFAF8— the wall every piece is shown against.
- Fog
#E8EAEA— shadowless depth; panels, hairlines, the backdrop gradient.
- Ink
#1A1C1E— warm-neutral near-black; all text, one weight of authority.
- The field
- Cyan → violet → rose → amber. Never applied to UI — it exists only behind or inside glass.
The discipline is the point: if the prism hues ever touched a button or a heading, the hero's claim ("colour lives only in the glass") would be a slogan instead of a fact.
Fraunces, blown thin
Display is Fraunces at optical size 144 and weight 300 — at that end of its axes the hairline contrast turns glassy, and the italic swashes bend like hot rods of crystal. One italicised phrase per heading is the typographic bravery budget; the giant outlined VERRE in the footer spends the rest of it. Inter carries body and the tracked small-cap labels, set at a deliberate scale (11 / 13 / 17 px) so the serif display always wins the room.
One plane, two faces
The signature element is a three.js torus knot in MeshPhysicalMaterial with
transmission: 1, lit by a PMREM-filtered RoomEnvironment. Behind it
sits a single shader plane with a split personality. three.js renders transmissive scenes in
two passes: first the opaque scene is drawn into an internal buffer that the glass then
samples for refraction, then the scene is drawn to screen. Inside the plane's
onBeforeRender, renderer.getRenderTarget() is non-null only during
that internal pass — so a uPass uniform flips the fragment shader between a
quiet white-to-fog gradient (what you see) and a vivid drifting colour field (what only the
glass sees). Colour therefore reaches the eye exclusively through refraction. Pressing the
hero briefly lerps the two faces together, letting you glimpse the bare field.
- Glasstransmission, thickness 1.5, IOR 1.5, clearcoat, and iridescence standing in for dispersion (the real dispersion parameter postdates r160).
- Fieldfour Gaussian blobs in GLSL, hue-rotating over minutes; one blob is lerped toward the cursor so the refraction answers your hand.
- Vesselsthe catalogue is hand-drawn SVG paths — grey gradients at rest, each piece's cased colour fading in on hover. Same rule, smaller stage.
- Lensthe "refraction test" divider is a prism gradient under a CSS radial mask that follows the pointer along a hairline.
- MotionGSAP line-mask intro and ScrollTrigger reveals,
all on
power3/power4.out; one scrubbed parallax on the hero stage.
Three passes with a fine-tooth comb
Pass 1 — Craft
- The hero canvas met the page with a visible seam: the backdrop plane was being
ACES-tonemapped while the page was not. Removed tone mapping from the plane's shader and
authored its colours in exact linear equivalents of
#FAFAF8/#E8EAEA, with the gradient easing back to page-white in the bottom 14%. The seam is gone. - The knot sat on a fixed x-offset and collided with the headline at middling aspect ratios. Its position and scale are now derived from the camera frustum per resize.
- Strengthened the hidden field's saturation (the first build refracted skim milk) and lowered glass roughness 0.06 → 0.03 for a harder, wetter surface.
- Copy tightened: every price in DKK with tabular numerals, one metadata line per piece, banned filler adjectives.
Pass 2 — Depth
- Added press-to-peek: pointerdown on the hero drives a smoothstep envelope on a
uRevealuniform, crossfading the quiet face into the bare colour field for ~2.5 seconds — the site briefly confesses how the trick works. - Gave the knot a 1.4% breathing scale and pointer-coupled tilt so it reads blown, not lathed.
- Second-read details: the catalogue vessels keep their colour secret until hover, and the spectrum-lens divider rewards a wandering cursor between Process and Studio.
- Retimed the intro: line masks on
power4.outat 1.5s with 0.14s stagger, supporting copy trailing by half a second.
Pass 3 — Hardening
- Fraunces' long italic descenders were clipped by the line-reveal masks — fragments of the g-tails floated under the headline. Masks now carry .32em of forgiveness.
- On portrait screens the knot filled the viewport behind the text; the true extent of a (2,3) torus knot is ~1.95× its nominal radius, not 1.45×. The narrow layout now measures the copy block's offset and fits the knot into the space above it. Verified at 375, 768, 1440.
- Chanel rule: removed the catalogue cards' hover lift and shadow — the colour reveal is the interaction; the jump was noise.
- Reduced motion: CSS media query kills keyframes and transitions; JS renders one static
frame, skips GSAP, and never starts the rAF loop. The loop also pauses on
document.hiddenand when the hero scrolls out of view; DPR is capped at 2; WebGL failure falls back to a CSS-gradient glass ring.