Atelier — demo-site engine for local venues
Finds local venues without a website and generates a brand-true 3D demo site, ready for consent-based outreach.
- Node.js
- Three.js
- motion
- Lenis
- SQLite
- Cloudflare Pages
- node:test
- 1,152text/background pairs checked across 192 palettes, none below WCAG AA
- 4 daysfor two full implementations, Codex then Claude Code
- 100Lighthouse accessibility, best practices and SEO on the fictional test site
From a venue with no website to a 3D demo
Finds a venue with no website, turns its real photos, menu and posts into a brand-true demo site with a 3D scene, and writes to it only after a double opt-in.
Tap a block to see what it does.
- Input
- Logic
- Infrastructure
- People
- Data
Input
Prospecting
Finds venues with no website in OpenStreetMap through the free Overpass API (Google Places is only a stub) and scores each lead from 0 to 100.
- OpenStreetMap
- Overpass API
Hands to Brand DNA, Local CRM
Logic
Brand DNA
Turns the venue's real site, posts, photos and menu into tagged facts; the accent colour is measured on its photos in OKLab, not guessed.
- OKLab
Gets from ProspectingHands to Design system
Logic
Design system
8 archetypes kept as data, 7 'gesture' recipes against templated sites, and a match over 192 palettes that only emits WCAG AA text pairs.
- DESIGN.md
- WCAG AA
Gets from Brand DNAHands to Site generator

Logic
Site generator
Builds 12-14 sections from a 16-section catalogue (real menu, gallery, sourced reviews, hours) plus one 3D signature scene.
- Three.js
- motion
- Lenis
Gets from Design systemHands to Quality gate

Logic
Quality gate
Rules R21-R35 and a richness check block a site with no real menu or fewer than 6 gallery items; an auditor and Lighthouse run before release.
- Lighthouse
- node:test
Gets from Site generatorHands to Pages deployer
Infrastructure
Pages deployer
Uploads to Cloudflare Pages with no dependencies (hand-written BLAKE3), dry-run by default; it never touches DNS, so its token needs no DNS rights.
- Cloudflare Pages
- BLAKE3
Gets from Quality gateHands to Consent gate
People
Consent gate
Email only after the venue confirms a double opt-in, enforced by a SQLite trigger; a real send needs both an explicit flag and the API key.
- Resend
- SQLite
Gets from Pages deployerHands to Local CRM
Data
Local CRM
SQLite with 11 tables, 4 triggers and append-only history; the local dashboard reads it without exposing any email address.
- SQLite
- node:sqlite
Gets from Consent gate, Prospecting
The problem
Many good local venues have no website, or a weak one. A ready demo built on their own brand sells better than a cold pitch, but hand-made demos cost too much, and in Italy promotional email generally needs prior consent.
What I built
- Set the order of work after the first three generated sites came out flat: first one master design skill, then a single test site, only then more examples.
- Directed two full builds in four days: Codex wrote v1 as a TypeScript monorepo, then Claude Code with five parallel agents wrote v2, a framework-free Node engine of about 23,000 lines.
- Made the engine start from the venue itself: its real site, posts, photos and menu become facts tagged observed, inferred, unknown or conflicting, and palettes are matched so every text pair passes WCAG AA.
- Put the legal rules in the database: a SQLite trigger refuses any email without a confirmed double opt-in, sends run only Tuesday to Thursday 9:30-11:00, and every command defaults to dry-run.
The hard part
Deploying to Cloudflare Pages with zero dependencies. Pages addresses assets by a BLAKE3 hash, which Node does not ship, so BLAKE3 was written from scratch and checked against the official test vectors, including the 1,024-byte chunk boundary where a wrong implementation fails silently.
Result
The whole chain runs locally and is covered by tests and dry-runs; the fictional test site passes the quality gate and scores 100 in Lighthouse for accessibility, best practices and SEO. By design nothing went out: no demo published and no email sent until credentials and a legal review are in place.
My role
Personal project. I set the vision, the constraints and the order of work, supplied the reference material and judged every output; Codex (v1) and Claude Code with parallel agents (v2) wrote the code.
