The concept
Blacktop Ballroom is a 400-cap rock and roll room — the loud, sweaty counterpart to a quiet listening bar. The page's job is this month's bills and tickets, carried with the energy of a stapled telephone-pole flyer elevated to art. Two ideas rule everything: a working bulb marquee for a header, and a poster wall where every gig poster is generated in code — torn edges, halftone, sticker slaps — re-randomizable like a fresh print run.
The two photographs (the pit under red/blue wash, the backstage rig) were generated with Nano Banana Pro. Everything else on the page — every poster, the marquee, the barcodes, the torn dividers — is drawn by code.
Palette
- BLACK LEATHER#0E0C0A
- MARQUEE AMBER#FFB64D
- SIGNAL RED#E33224
- STAGE-WASH BLUE#3E8EE0
- STICKER WHITE#F4EFE6
Amber is the house light — it belongs to the marquee and to wayfinding. Red and blue are stage wash: they only appear where energy peaks (sold-out slashes, the barricade line, hero words). Sticker white is the paper every poster is printed on.
Type pairing
plays too loud
Big Shoulders (900, tight-stacked, all caps) is a condensed American grotesk built for Chicago's flag — it reads like wood type on a rock poster, which is exactly the job. Special Elite is a worn typewriter face: every date, set time, and house rule looks hammered onto the flyer by whoever ran the door that night. Poster voice + office voice. Nothing else.
How the signature works
1 — The bulb marquee (canvas). The header is ~700 individual lamps. Letters are a
5×7 dot-matrix font; a ring of border bulbs chases around them. Drawing a radial-gradient glow per
bulb per frame would melt the frame budget, so all 16 brightness levels of one bulb are pre-rendered
into a sprite atlas and each lamp just stamps its level with drawImage:
// pre-render 16 brightness levels once…
for (let i = 0; i < 16; i++) drawBulb(atlasCtx, i / 15);
// …then per frame, each bulb is one cheap blit
const breathe = 0.86 + 0.14 * Math.sin(t * b.speed + b.phase);
ctx.drawImage(atlas, level * tile, 0, tile, tile, b.x - tile/2, b.y - tile/2, tile, tile);
Two bulbs per line are deliberately "wired badly" — they drop out at random and glow red-hot when
dying, like every real marquee you've ever loved. With prefers-reduced-motion, the loop
never starts: one fully-lit frame is drawn and left alone.
2 — Generative gig posters (seeded SVG). Each show gets a seed; a mulberry32 PRNG
drives every decision — press scheme (five paper/ink/accent setups), torn-edge jitter, halftone burst
position, sticker rotation, staple angles. Band names are stacked and slammed to the full measure with
textLength, so every word justifies like wood type no matter its length:
// torn paper: jittered points along each edge, then a clipPath
for (x = 6; x < w - 6; x += 16) pts.push([x, 6 + rnd() * 5]);
// wood-type justification: every word fills the measure
<text textLength="260" lengthAdjust="spacingAndGlyphs">DENT CITY</text>
"Run another print" bumps the run number, reseeds all eight posters, and the wall re-inks itself — same bills, fresh print.
3 — The power chord (Web Audio). Strictly user-initiated. Six sawtooth oscillators
(A5 power chord, doubled and detuned ±6 cents) run through a WaveShaper distortion
curve at drive 380, a lowpass "cab" at 3.4 kHz, and a fast-attack long-decay envelope, with 30 ms of
noise for the pick:
curve[i] = ((3 + k) * x * 20 * (Math.PI / 180)) / (Math.PI + k * Math.abs(x));
shaper.oversample = "4x"; // keeps the fuzz from aliasing
Three passes
- PASS 01Correctness & composition — the two-line marquee ate the whole desktop fold, so wide screens now get a one-line sign with a width cap; tightened the glow atlas so letters read crisp instead of mushy; freed the hero kicker from the paragraph measure; rotated the five press schemes so adjacent posters never share ink.
- PASS 02Elevation — moved the SOLD OUT slap below the band name so the headliner stays readable; added cursor heat (lamps flare as the pointer passes) and a nine-second power sag where the whole sign browns out for a beat; gave the strike button a warmer invitation.
- PASS 03Taste — Chanel rule: stripped the repeating pointing-finger dingbats from these very headings (the reprint button keeps the only one); re-checked 390 px seriously; confirmed reduced-motion serves a lit-but-still marquee and unrotated posters.
Do this yourself
- Pick one loud object from the subject's world — ours is the bulb marquee — and make it the header. Everything else stays disciplined around it.
- Write the copy first, like the venue's door person wrote it. "Earplugs free at the bar" designs the page more than any gradient will.
- Choose two faces with jobs: one poster voice (condensed, 900, stacked caps), one office voice (typewriter mono for dates, prices, rules). Ban everything else.
- Make the content generative: seed a PRNG per item and derive every visual decision from it, so a "reprint" button can regenerate honest variety in one click.
- Pre-render what repeats. Hundreds of glowing bulbs = one sprite atlas + cheap blits, not per-frame gradients.
- Ask Claude to screenshot and critique three times — composition, then elevation, then subtraction. Remove one accessory at the end.
- Respect the quiet users: reduced-motion gets a still-but-lit page, sound only ever starts from a click.