zsty.us

Before / After · Case Study

Velcro.music — a record-label hero that actually swings

Rebuilt velcrowrecords.com — a 2,338-line single-file static site on Netlify — into velcro.music on Next.js 16. The signature chain-and-skull pendant used to be a flat image on a fixed CSS keyframe; now it swings on a real verlet pendulum through a drifting SF-fog band, with the skull's diamond eyes glinting at each apex. Under the hero: a typed product catalog, consent-first notifications, and a hardened header set — plus a per-link articulated-chain engine reconstructed from the single product photo.

A label should feel alive the second you land on it. This one swings.
🔒velcro.music
jackiej.events — modern site

The motion diff

Press play on both. That’s the whole pitch.

Left: the legacy hero — one flat image of the chain rotating on a fixed CSS keyframe, on a plain black stage. Right: the rebuild — the pendant swings on a real verlet pendulum (held forever by an energy thermostat, never a hard loop), a volumetric SF-fog band drifts behind it with the VELCRO wordmark surfacing out of the haze, and the skull’s diamond eyes glint at each swing apex. Runs live in the browser at 60fps — the production page ships no video at all.

🔒velcrowrecords.com
Before
velcrowrecords.com — one image, fixed CSS-keyframe swing
🔒velcro.music
After
velcro.music — verlet pendulum + SF-fog + diamond-eye glints

Receipts — measured

Numbers that moved.

0
Security headers (CSP, HSTS, …)
was 0
1
Single HTML file → typed App Router files
was 1
0
Products modeled as validated records
was 0
  • Single-file 2,338-line Netlify/Snipcart site → Next.js 16 App Router on Vercel
  • Hero: fixed 4s CSS-keyframe swing → verlet pendulum + drifting SF-fog + diamond-eye glints, 60fps
  • Per-link articulated-chain engine reconstructed from one product photo — pixel-exact at rest
  • Hardened: CSP + HSTS + 5 more headers; products as Zod-validated records; reduced-motion fallback

The four beats

Problem · Insight · Build · Outcome.

Scroll past the legacy frame. The four beats land in order. At the end, the modern site fades into the same window.

🔒velcrowrecords.com
Legacy
01 · Problem

A 2,338-line single HTML file doing everything

Legacy velcrowrecords.com crammed head meta, CSS, product data, modal logic, the newsletter, video, Snipcart config, and JSON-LD into one Netlify-hosted file. The signature pendant swing was a fixed CSS keyframe rotating one flat image — clean, but it never felt alive, and nothing in it was type-safe or reusable.

02 · Insight

A label sells a feeling — the hero should breathe

The swing is the brand. Real pendulum physics with no hard loop, fog rolling through the wordmark, glints firing at the apex — small touches that make a 30-second visit feel intentional. And the chain itself could be more than a rotating rectangle.

03 · Build

Next.js 16, physics on canvas, a real data layer

Split-architecture rebuild on Next.js 16 + Vercel: a verlet-pendulum hero held by an energy thermostat, a layered SF-fog band, and an optional per-link articulated-chain engine reconstructed from the single product photo (pixel-exact at rest). Behind it, a Zod-validated catalog, consent-first notifications, and a CSP + security-header set the legacy file never had.

04 · Outcome

Live on velcro.music — and it swings

The public hero swings on real physics through drifting fog with glints on the beat, the storefront and notification plumbing sit ready behind it, and the whole thing ships from a typed, split codebase instead of one hand-edited file.

🔒velcro.music
Modern

Architecture

One chain replaced by another. Receipts above.

Old stack
  • Single-file HTML
  • Netlify
  • Snipcart
  • CSS-keyframe swing
  • No type safety
New stack
  • Next.js 16
  • React 19
  • Tailwind v4
  • Vercel
  • Verlet hero physics
  • Zod catalog

What changed

Grouped by what kind of system shipped.

Each claim ships with concrete evidence — env vars, table names, cadence chips. No marketing fluff.

Design

A pendant that actually hangs there

The legacy swing was a fixed CSS keyframe — one rigid image rotating on a hard 4-second loop. Rebuilt as a JS verlet single-pendulum pivoting at the clasp, with an energy thermostat that holds the amplitude forever: it never decays to rest and never repeats exactly, so it reads as a real hanging chain. The drop-shadow and a background cast shadow sweep in sync, and the pendant takes a subtle parallax lean toward the cursor.

Before
One image, linear 4s CSS keyframe, ±8°.
After
Verlet pendulum — never decays, never a hard loop.
  • src/app/page.tsx — verlet integrator + per-frame energy thermostat (g≈0.0007, ±~8°)
  • Asset-separated layers: a static chain-top over the moving pendant so the top link sits over the bail (no shear)
  • prefers-reduced-motion → static hero, no JS swing
Design

SF-fog atmosphere + motion-synced diamond glints

A layered smoke band drifts behind the chain, with the VELCRO / RECORDS wordmarks materializing out of the haze and dissipating as they cross. The skull's two diamond eyes glint cyan at each swing apex — tied to the pendulum, not a fixed timer — so the sparkle fires exactly when the chain is at the top of its arc.

Before
Plain black stage, no atmosphere.
After
Volumetric fog + glints that fire on the swing.
  • SmokeBand: staggered drifting cloud/ring/word puffs with negative delays (globals.css)
  • velcroEyeGlint keyframes peak at the swing extremes (0 / 50 / 100%)
  • Every effect has a prefers-reduced-motion branch
Agent backbone

A per-link chain engine, reconstructed from one photo

Beyond the shipped hero, built a canvas engine (reachable at ?fx=rope) that slices the single product photo into per-link sprites along the traced chain strands plus a rigid medallion body, then drives them with a verlet rope and a kinematic pendulum so the chain articulates link-by-link with motion-coupled sheen and sparkle. The front necklace was isolated from the photo with a circle-fitted medallion rim and whole-link reconstruction where the second chain wove through it.

Before
The whole pendant was a single rotating image.
After
The front chain articulates per link; diamonds catch the swing.
  • scripts/build_rope_rig.py — offline sprite atlas + rig.json builder
  • Rest pose recomposites the source photo pixel-exact (0 px over threshold on black)
  • 60fps at 1440 and 390 via capped DPR + adaptive quality tiers
Retention

A real storefront + consent plumbing under the brand surface

Products are modeled as Zod-validated records — one source of truth for the UI, JSON-LD, the cart adapter, and AI-agent retrieval. Three independent notification channels (web push via VAPID, email with double-opt-in, SMS with STOP/HELP) record IP-hash consent with a copy version. Legal routes, robots, and sitemap ship from the same typed layer.

Before
Everything inlined in one HTML file; no validation, no headers.
After
Typed data layer, consent-first notifications, hardened headers.
  • src/data + Zod schemas — a malformed product fails the build, not runtime
  • src/lib/notifications — push / email / sms repositories + consent versioning
  • next.config.ts — CSP, HSTS, X-Frame-Options, COOP, and 3 more headers

← All rebuilds

Velcro.music — a record-label hero that actually swings — zsty.us