Where it started
On 11 October the site was a name, a short bio and a list of links. The Playground had three experiments: Particle Architecture, Shader Lab and an early ASCII World. The contact page still said "Start a Project", which made a personal site read like a services page.
I wanted somewhere to put things I make for fun and to show how I work as a designer who also ships the code. The brief I wrote for myself was short: phone first, fast, honest about being a playground, and easy to keep adding to without the whole thing rotting.
Discovery
Before designing anything new I audited what was there. The findings were not glamorous, but they set the whole plan.
- style.css was 42 KB of overrides stacked on overrides. Nobody could say which rule was winning.
- Particle Architecture was one 60 KB HTML file with 25
!importantrules and 15 media queries. - Only Shader Lab had tests. Every other page could break silently.
- The copy pointed the wrong way. "Start a Project" invited client work I wasn't looking for.
- Analytics was on a paid trial plus a session recorder, which was heavier than a small site needs.
From that I set five working rules: mobile first at 360 to 430 px, colours and sizes only from tokens, no build step, preview every new feature before it reaches main, and turn every rule into a test so it can't drift.
Foundations first
The shared layer is three files loaded in a fixed order: tokens, layout, components. Every tool loads them, even the ones with their own look, and overrides them with selectors scoped to its own body class instead of dropping them.
Small rules carry a lot. Internal links get a straight arrow, external links a diagonal one, and a link never shows two arrows. Every static button gets the same class, so focus states and sizes stay consistent across ten very different pages.
Then the cleanup. I rewrote style.css from 40.5 KB to 18.2 KB and checked it with 37 before and after screenshots across five pages, three viewports and every hover and error state. All of them were pixel identical, which was the only way I was willing to delete that much CSS.
What's in the Playground
Nine experiments: two games, one growing collection of creatures and six tools. Every one runs in a phone browser with nothing to install, and files you add stay on your device.
GameAshen VigilA six minute 1-bit horde survival run. Pick one of five heroes, lead the party or go solo, and hold the old road until dawn.Drawn through WebGL from one texture atlas so it holds 60 fps on mid-range phones. Scores go through server-side checks before they reach the ledger.PLAY
GameLAST SIGNALA portrait-first ASCII survival shooter with a touch-anywhere joystick, upgrades, the Warden boss and a global leaderboard.Everything in the world is text, down to the grass, the rain and the health bars. The heroes started as painted sprite sheets and ended as rigs rebuilt glyph by glyph every frame.PLAY
CreaturesBestiaryCreatures from my ink sketchbook as animated 1-bit sprites. ORIGINAL flips back to the drawing they came from.Each creature is a small JSON rig baked by a Python script, so adding one needs no page code.MEET
ToolChroma WheelA hue ring and brightness square with colour harmonies, channel locks, a gamut mask and a shades ramp.It also ships as a Photoshop plugin that keeps the foreground colour in sync both ways.OPEN
ToolLiquid GridA layout sketcher for brochures, magazine spreads and posters. Every box shares its edges with a neighbour, so dragging one gap hands the space to the box beside it.Exports a real-size vector PDF and an editable InDesign file. The export libraries only load when you press export.OPEN
ToolLiving ASCIITurns looping scenes, your own videos and photos into animated ASCII art, with controls for motion and glyph sets.Video and photos are processed in the browser and never leave the device.OPEN
ToolSubject ScannerA 3D subject redrawn as halftone dots or ASCII: a public domain marble bust, a sound-reactive anomaly or your own photo. Hold the crosshair on hidden points to scan them.The bust scan is cut down to about 42k triangles so a phone can turn it smoothly.OPEN
ToolParticle ArchitectureGenerative particle artwork and 4:1 banners, optionally guided by a reference image, with image and vector export.The oldest tool and still one 60 KB file. It's next in line for a careful split.OPEN
ToolShader LabA layer editor for procedural visuals: 17 procedural modes, 19 image effects, plus text, shape, 3D and video layers in five formats.The only tool that had browser tests from day one, and the model the rest of the site now follows.OPEN
Beyond the Playground
The token thinking on this site comes from a bigger piece of work: a pipeline I built for a product team that takes a brand from Figma to running code without anyone retyping a value. It isn't on this site, so the names are left out, but the approach is the same.
- Step 1Token builderA Figma plugin that writes the brand's tokens as Figma variables: about 743 of them in four collections. Primitives hold the raw values, and semantic names like surface, text or border point at primitives instead of repeating them.
tokens.json - Step 2Component builderA second plugin that builds 61 components and patterns on those variables, with full variant matrices (a button alone is 36 combinations of appearance, size and state) and a report that flags any value not bound to a token.
components.json - Step 3Code generatorA command line tool that reads both files and generates the front-end components with their styles, tests and Storybook stories.
components + stories
Two decisions made it work. Components may only use tokens, so a brand change is a token change and nothing else. And every generated file carries an ownership header: delete it and the file becomes hand-written, so the next sync leaves it alone. That let designers keep regenerating while developers kept polishing.
LearnedA component in Figma should carry a contract (names, variants, tokens), not try to be a pixel clone of the code. The latest report came back with no clipped text, no missing token paths and no failed components.
What got hard
Each of these started as a problem in front of me, not a plan. Most of them include something I built, tried and threw away.
Phones are the real test
Ashen Vigil, a 1-bit horde survival game, ran fine on my machine and stuttered on a Galaxy A71. I moved it from the 2D canvas to WebGL with one texture atlas, about one draw call a frame, and kept 2D as a fallback. The bar is now 60 fps on a phone viewport with the CPU throttled six times. Then some phone GPUs drew a black field because they rounded texture coordinates by a few texels, so the white texel every fill uses is now pinned to the atlas origin.
Tried and droppedA darkness layer with dithered light pools. It looked moody and made the game too dark and too slow on phones.
Looking like someone else
The first version of Ashen Vigil was too close to another studio's 1-bit isometric look. I kept the mechanics and the 1-bit style and changed what made it recognisable: no isometric grid, a winding road over cracked earth, ash piles where enemies fall, and one colour, ember orange, reserved only for fire. The run now ends at dawn, with the horizon catching and the horde crumbling to ash.
LearnedCheck references before the first public version, not after.
Ink drawings to pixels
Bestiary turns my ink drawings into animated 1-bit sprites with a small Python pipeline. The first rigs moved rectangles of the image. It worked, and it looked like a head cut at the mouth moving up and down. The newer warp rig bends the full resolution drawing with a smooth displacement field and only then downsamples it, so jaws open along the real line of the mouth and fingers curl without seams.
Tried and droppedA trace mode with crisp 1 px lines (it lost the ink), and a negative dark view (too harsh).
One interface, many looks
The VIEW menu switches between Normal, Pixel and ASCII, and four colour themes sit on top. All twelve combinations come from the same tokens, so a new page gets them for free. One rule kept it sane: modes change the interface, never the art. Playground covers stay exactly as they are in every mode.
A browser test opens every theme in every mode on four pages and fails if body text drops below 7:1 contrast or anything scrolls sideways.
Tried and droppedRe-rendering the covers in pixel and ASCII. And a full Ink mode, paper and pen-drawn rules, which I liked and switched off because it felt unrelated to the rest of the site. The code is still there.
Measuring without being creepy
When the analytics trial ended I removed it and the session recorder, and moved to Umami: cookieless, EU hosted, free at this size. One small loader holds the ID and only reports from the live domain, so local servers and previews stay out of the numbers. Events are named after what a person did (picked a theme, opened a card, finished a run), never what they typed.
At one point a tracking tag got pasted straight into a page and every visit counted twice. The smoke test now fails on that, and adding ?notrack to any URL keeps my own visits out.
Readable at a glance
A playtester said the enemies blended into the party. Three ink tones fixed it: the hero in bright ink, companions a step paler, the hollow in ash grey.
Tried and droppedDark silhouettes for enemies. Clearer on paper, too dark in play.
How it ships
- New features go on a work branch.
- I review a private preview on my phone.
- Only then does it merge to main and go live.
- Small fixes to live features can go straight to main.
Two test suites run on every push. A source check covers routes, metadata, gallery order, share images, the sitemap and analytics. A Playwright run at phone size covers view modes, themes, contrast, the 404 page and lazy loaded covers. Every change gets a line in the changelog.
Where it landed
- 9experiments, two of them games
- 12looks from one token set
- −55%CSS, zero visual change
- 60 fpswith the CPU throttled 6x
Traffic numbers aren't here yet. Umami has been live for a few days, and I'd rather wait for a real week of data than quote a guess.
Next
Review the first week of data and drop the events nobody needs. Set up a funnel from Home to Playground to a game. Move the two older Bestiary creatures to the warp rig. And keep adding to the Playground, now that adding something new is a checklist rather than a risk.