Design Notes · Site 13 of 25

How The Velvet Hour
Was Made

A basement jazz club, built from strokes, gradients and one canvas of smoke.

Concept

The Velvet Hour is a fictional late-night jazz club fourteen steps under Murray Street, Perth — ninety seats, candlelight, a house piano, no sign on the street. The audience is anyone deciding where tonight ends. The page has one job: make you want to find the amber lamp and walk down, then hand you the week's setlist before you change your mind.

Everything on the page is procedural — SVG linework, CSS gradients and a 2D canvas. No images, no video, nothing raster.

Palette

Warm, dim, expensive. One near-black, one velvet, two metals — nothing cold anywhere on the page.

House Black#140A0EThe room with the lights down; a black with blood in it, not a screen black.
Burgundy#3D0F1FThe velvet itself — banquettes, curtain, the panel behind “The Room”.
Gold#D4AF6ABrass rail and frame linework. Structure only — it never fills a surface.
Candle#FFDFA3The hottest light in the room: hovers, highlights, the glow behind the pendant.
Cream#EBDCC8Body text. Roughly 13:1 on house black, so long copy stays effortless.

Typography

Marcellus — The Velvet Hour

Lora italic — fourteen steps below the street, the night keeps its own time.

Marcellus is a single-weight roman with flared, inscriptional terminals — letterspaced in caps it reads as engraved deco brass without resorting to a novelty display face. It carries every label, tab and heading; hierarchy comes from size, tracking (.14em–.44em) and case rather than weight. Lora does the talking: a bookish text serif whose italic has enough swing for lead lines and drink descriptions. The one moment of bravery is the hero — THE at .24em size but .92em tracking, stacked over VELVET / HOUR at clamp-scaled 6.9rem, drawn letter by letter.

Techniques

The signature — a frame that draws itself. The hero is wrapped in an art-deco frame assembled from nine SVGs: four corner ornaments (one drawing, mirrored with CSS transforms), four edge strips, and a pendant medallion hung from the top rule. Every path carries pathLength="1" so stroke-dasharray: 1 normalises to “one dash the length of the path”; a GSAP timeline then staggers stroke-dashoffset from 1 to 0 — edges first (1.7s, power2.inOut), corner fans next, pendant last — so the gold appears to be traced by hand around the room. The edge strips use preserveAspectRatio="none" and contain only straight lines, so stretching them between fixed-size corners distorts nothing.

Smoke. A single hero canvas drifts 14–22 pre-rendered radial-gradient sprites left to right with sinusoidal rise and phase-offset alpha, composited with lighter at very low opacity — haze, not clouds. The pointer applies a gentle “wind”. DPR is capped at 2; the rAF loop pauses on document.hidden and when the hero leaves the viewport via IntersectionObserver; reduced motion gets one static frame.

The setlist card. A CSS 3D table card (perspective 1600px) whose two faces are re-rendered from data before each cumulative 180° rotateY, so the card keeps flipping the same direction like a table tent being turned. A separate wrapper adds a ±5° pointer tilt on fine pointers.

Everything else is GSAP ScrollTrigger reveals, pure-CSS equalizer bars, and one CSS keyframe glint that travels the top rule every eleven seconds. Libraries: GSAP + ScrollTrigger only.

Iteration Log

  1. Craft

    • Edge SVGs didn't stretch between the corners (absolutely-positioned replaced elements ignore left+right for width) — rebuilt with explicit calc() widths so the frame actually closes.
    • The medallion originally straddled the top rule behind a background-coloured mask that read as a dark box over the hero gradient — redesigned it as a pendant hanging from the rule; no mask needed.
    • Nav links collided with the medallion — split the nav symmetrically around the centre, VH monogram left, live marker right.
    • Softened the smoke sprite (larger radii, lower core alpha) so it reads as haze rather than blobs; tinted the scrollbar to the palette.
    • Tightened hero eyebrow tracking and size under 480px.
  2. Depth

    • Candlelight behind the pendant: a radial glow that breathes over seven seconds, so the signature keeps living after it draws.
    • Pointer tilt on the setlist card — it sits loose on the table like a real tent card.
    • Second-read details: a faint coaster ring stamped at a slightly different spot on each night's card, and a gold glint that travels the top rule every eleven seconds.
    • The live equalizer picks up tempo when you hover it.
  3. Hardening

    • 375px: hero meta line had no soft-wrap points between its nowrap spans, forcing the whole hero column off-centre — fixed, and verified zero horizontal overflow.
    • Chromium renders dashed garbage when vector-effect: non-scaling-stroke meets pathLength normalisation — removed the vector-effect and compensated corner stroke width under 640px instead.
    • Night tabs: roving tabindex plus Arrow/Home/End keyboard support.
    • Reduced motion verified end-to-end: strokes pre-drawn, one static smoke frame, transitions and keyframes killed in CSS and guarded in JS.
    • Chanel rule: removed the two diamonds flanking THE in the hero — the pendant above already says it.