Yuzhe's Blog

yuzhes

creative-lab: Shipping a Visual Demo Every Two Days

creative-lab: Shipping a Visual Demo Every Two Days

GitHub: bkmashiro/creative-lab · Live: cl.yuzhes.com


The Premise

Creative coding as a daily (well, every-two-days) practice. Ship something visual. Keep it self-contained. Don’t think about it too hard.

That’s the rule. Each demo in creative-lab is a single HTML file: no npm, no bundler, no build step. Open it in a browser and it runs. The gallery index is a dark-themed grid that loads every demo inline.

There are 12 demos so far. Number 13 is probably being written right now.


The Demos

#NameTechnique
001Particle PhysicsCanvas 2D, Verlet integration
002Fractal TreeRecursive canvas drawing
003Wave InterferenceSuperposition, 2D field
004Mandelbrot ExplorerWASM / pure JS, zoom, coloring
005Cellular AutomatonConway’s GOL variant, canvas
006Audio VisualizerWeb Audio API, FFT, canvas
007Gravity SimulatorN-body, Barnes-Hut approximation
008Cloth PhysicsSpring-mass system, canvas
009Ray MarchingSDF, WebGL fragment shader
010L-System TreesLindenmayer grammar, SVG
011Reaction DiffusionGray-Scott model, canvas
012Fluid SimulationSPH particles, WebGL

Each file is between 100 and 300 lines. Some are pure math, some are GPU-bound. All of them run without a server.


Single-File Philosophy

The constraint is intentional. A single HTML file is:

The demos inline everything: CSS in <style>, JS in <script>, assets as base64 if needed. For WebGL demos, the shaders are template literals inside the JS. It’s not elegant in the traditional sense, but it’s self-contained in a way that a webpack bundle never quite is.

<!DOCTYPE html>
<html>
<head>
  <style>
    body { margin: 0; background: #0a0a0f; }
    canvas { display: block; }
  </style>
</head>
<body>
<canvas id="c"></canvas>
<script>
// entire demo in here, ~150 lines
const canvas = document.getElementById('c');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
// ...
</script>
</body>
</html>

That’s the template. Start there. End somewhere interesting.


The Cron Pipeline

The interesting part isn’t the demos themselves — it’s how they get generated.

Every two days, a cron job fires a Claude agent with the following context:

The agent writes the HTML file, updates the gallery index, and commits directly to the repo. Cloudflare Pages picks up the commit and deploys within a minute.

The todo list looks roughly like this:

# Demo Ideas

- [x] 001 Particle Physics
- [x] 002 Fractal Tree
...
- [x] 012 Fluid Simulation
- [ ] 013 Voronoi Diagram
- [ ] 014 Strange Attractors
- [ ] 015 Metaballs
- [ ] 016 Fourier Drawing
...

When the agent picks up the next unchecked item, it checks it off, generates the demo, and commits. The pipeline is entirely automated — I set it up once and it runs.


What the Agent Gets Right (and Wrong)

Most demos come out working on the first try. Simple particle systems, fractal trees, cellular automata — these are well-covered in training data and the agent generates clean, correct code.

The harder ones — fluid simulation, ray marching — sometimes need a nudge. The agent occasionally produces physically plausible but visually uninteresting results (e.g., an SPH fluid that technically conserves momentum but runs at 3fps and looks like a gray blob). When that happens, I regenerate with a more specific prompt (“make the particles smaller, increase the interaction radius, use a warm color gradient based on velocity”).

One thing the agent does consistently well: the visual style. Dark background, bright particles or lines, smooth animation. It’s picked up the aesthetic from the reference demos and applies it reliably.

One thing it does less well: performance. An agent-generated WebGL shader is rarely optimized. If the demo stutters, I usually drop into the file and fix the hot path manually — but the structure and math are almost always correct.


The gallery is a hand-written HTML file that loads each demo in an <iframe> for the thumbnail preview. It auto-discovers demos by reading a manifest.json generated at commit time:

[
  { "id": "001", "name": "Particle Physics", "file": "demos/001.html" },
  { "id": "002", "name": "Fractal Tree", "file": "demos/002.html" },
  ...
]

The manifest is updated by the agent as part of the commit. Clicking a thumbnail opens the demo full-screen. The layout is CSS Grid, 3-4 columns depending on viewport width.


Why This Works

The two-day cadence is deliberate. One day is too fast — there’s no time to think about what to make. One week is too slow — it becomes a “project” with expectations. Two days is enough to pick something interesting from the list, trust the agent to implement it, and move on.

The zero-dependency rule removes an entire class of decisions. No framework debates, no bundler configuration, no dependency updates. Each demo stands alone.

The goal isn’t perfect demos. The goal is the habit of shipping.

Twelve demos in. The list has another forty items on it.


Running It Locally

git clone https://github.com/bkmashiro/creative-lab
cd creative-lab
# just open any demo directly
open demos/001.html
# or serve the gallery
python3 -m http.server 8080

No build step. No setup. It’s just files.