← Back to Peony

[ Parable build guide ]

How Peony was built

A fireworks studio, rendered with fireworks. The hero is a canvas particle engine — shells that rise, decelerate, and burst into gravity-bound sparks with fading trails. Click the sky and you fire one yourself.

static · no build stepcanvas 2DCinzel / Inter / Space Monolight + darkreduced-motion aware

The idea

You cannot photograph a fireworks show and keep what makes it a show: the rise, the pause, the break, the fall. So the hero doesn't try — it runs one. The sky is a live canvas, always firing, and the single interaction ("click the sky") turns a viewer into the pyrotechnician for one shell. The whole brand promise — "we build the three seconds you'll remember" — is on screen, moving, before you've read a word.

The stack

One HTML file, one stylesheet, one script. No framework, no libraries — the engine is a few hundred lines of vanilla canvas 2D. Type is Cinzel (a ceremonial Roman capital, for the occasion), Inter for body, and Space Mono for the measured, technical voice a pyro crew actually uses (shell counts, durations, fall-out radii).

Signature technique — the fireworks engine

Two particle types. A shell launches from the bottom with just enough upward velocity to reach its target height, decelerating under gravity; at apex it's swapped for a burst of 60–100 sparks fired radially, each with its own drag and decay. The trails come from a trick: instead of clearing the canvas each frame, we paint a translucent dark rectangle over it, so old positions fade rather than vanish.

// the trail: fade last frame instead of clearing
ctx.globalCompositeOperation = 'source-over';
ctx.fillStyle = 'rgba(10,10,20,0.22)';
ctx.fillRect(0, 0, W, H);
ctx.globalCompositeOperation = 'lighter';   // additive — sparks glow where they overlap

Additive blending ('lighter') makes overlapping sparks bloom toward white, the way real ones do. "Willow" bursts get lighter gravity and slower decay so they hang and droop.

Details that matter

The booking form is a demo — it validates and confirms in-place but sends nothing. And, obviously: this is a design concept, not a licensed operator. Don't recreate any of it in the real world.

Ship it on GitHub Pages

gh repo create bswxyz/peony --public --source . --push
gh api --method POST /repos/bswxyz/peony/pages -f 'source[branch]=main' -f 'source[path]=/'

Relative paths and a .nojekyll file — it serves from the project subpath with no config at all.

← Back to Peony