A modern, motion-driven landing page for LUMINOX (placeholder name) — a festival / rave wear brand built around the idea of wearable frequencies. The site is a visual showcase that points buyers to the brand's Depop store.
Vibe: infrared / heat palette · real-time cymatics (sound-frequency standing waves) rendered in WebGL · big festival-poster type · buttery GSAP scroll choreography.
- Three.js (vendored) — a custom GLSL shader renders Chladni / cymatic figures in an infrared heat ramp. Used for the hero background, the interactive "Signature Frequencies" plate, and the newsletter section.
- GSAP + ScrollTrigger (vendored) — preloader, hero reveal, manifesto word-ignite, marquee velocity, product reveals, frequency counter.
- Vanilla JS modules — no framework, no build step.
- Self-hosted fonts (Anton / Space Grotesk / Space Mono) — no CDN dependency at runtime.
Everything is static. There is no build step — open index.html through
any static server.
npm install # only needed if you want to re-vendor three/gsap
npm run dev # serves at http://localhost:4173…or any static server (python3 -m http.server, npx serve, etc.).
A Playwright script checks both desktop (1440×900) and mobile (390×844):
WebGL is live, the shop renders, there's no horizontal overflow, and the
console is clean. It also writes screenshots to scripts/shots/.
npm run dev & # start the server first
node scripts/verify.mjsindex.html # all sections: hero, marquee, manifesto, frequencies, shop, drop, footer
css/main.css # infrared design system, mobile-first
js/cymatics.js # reusable WebGL CymaticsField (the frequency visuals)
js/products.js # commerce layer + DepopAdapter (see below)
js/main.js # orchestration: preloader, GSAP, shop render, nav
data/products.json # placeholder stock — edit this to change the shop
assets/ # favicon, OG image, self-hosted fonts
scripts/verify.mjs # headless-Chrome verification + screenshots
vendor/ # three.module.min.js, gsap.min.js, ScrollTrigger.min.js
Open data/products.json and edit the products array. Each item:
Set the store-wide Depop link + currency in the shop block at the top.
The goal is for the site to pull listings (images, titles, descriptions,
prices) straight from your Depop and drop an Order on Depop button on each.
The code is already structured for this — see js/products.js →
DepopAdapter.
Depop has no public REST API, so the realistic path is one of:
- Serverless proxy (recommended). A tiny Cloudflare/Vercel function that
reads your public shop page and returns JSON in the shape above. Then:
Nothing else changes — the UI is identical.
const adapter = new DepopAdapter({ liveEndpoint: 'https://your-fn.workers.dev/shop' });
- Scheduled job that regenerates
data/products.jsonon a cron and commits/deploys it.
In both cases, keep the mapping in DepopAdapter.mapDepopListing() so the rest
of the app never has to know where the data came from.
- Placeholders to replace: brand name (
LUMINOX), Depop URL (https://www.depop.com/luminox/), social links in the footer, and the product images. - Accessibility / motion: honours
prefers-reduced-motion(shaders hold a still figure, marquee + tickers stop). WebGL has a CSS-gradient fallback. - Performance: shader canvases cap device-pixel-ratio and pause via
IntersectionObserverwhen off-screen.

{ "id": "lx-001", "title": "Resonance Tee — 174Hz", "category": "tops", // drives the filter chips "price": 48, "frequency": 174, // shown big on the card; ties to the brand "tuning": "Foundation", "image": null, // null => generated thermal artwork; or a URL "palette": ["#3a0a2e", "#e0341d", "#ff8a1e"], // heat gradient for the card "status": "available", // "available" | "low" | "sold" "depopUrl": "https://www.depop.com/luminox/" // where "Order on Depop" goes }