← All writing
// ENGINEERING NOTE BRIEF // RELIABILITY · 30-SEC BRIEF · 5-MIN FULL POST

My Logo Heals Itself. Then I Erased It Into a Stick.

A local model can’t recover information that isn’t there: give it a dumb global supervisor that knows when to restart.

“No amount of training fixes this. The information just isn’t there anymore.”

  • An 8,320-parameter neural cellular automaton grows the GR monogram on this site and heals it; every pixel only sees its neighbours.
  • Trained on round bites, it regrew the G as a second R after a cut. Retraining from scratch on cuts and scribbles, the damage visitors actually do, fixed it.
  • Erased down to a strip, it became a stick forever. The fix is a ~280-line supervisor that compares the shape to GR and regrows it honestly.

This week I erased my own logo down to a thin strip and waited for it to heal.

It thought about it for a while. Then it turned into a stick. Not a G, not an R. A long horizontal stick, and it stayed a stick forever.

That was awkward, because the whole point of this logo is that it heals.

What this thing is

On rossev.dev there's a "GR" monogram that nobody drew. It grows from a single pixel. Drag your cursor across it and it grows back.

Under the hood it's a neural cellular automaton, after Mordvintsev et al., Distill 2020. I reimplemented it from scratch in PyTorch. The short version:

  • 56×56 grid, and every cell holds 16 numbers: RGBA plus 12 hidden channels the network invents a use for.
  • Each step, a cell looks at its 3×3 neighbours through identity and Sobel filters, and a tiny network (48 → 128 → 16) decides how to change it.
  • Only a random half of the cells update on each step, and cells with no living neighbour die.

That's 8,320 parameters. Every pixel runs the same network and only sees its neighbours. Nowhere at runtime is there a picture of "GR".

And that last sentence is exactly why I got a stick.

First it learned to write "RR"

The paper trains healing on round bites. My first model learned bites perfectly.

Then I watched real people use it. Nobody bites a logo. They drag the cursor across it. They cut it.

One vertical cut between the letters, and the model regrew the G as a second R. My portfolio proudly said "RR".

Fine-tuning on cuts didn't help. At two learning rates it kept stubbornly growing the lime block from behind the R into the G's spot. What helped was retraining from scratch on damage that looks like what users actually do: vertical, horizontal and diagonal cuts, scribbles, and some bites. 12,000 iterations, 48 minutes on an M3 Pro.

Lesson one: train on the damage your users actually do, not the damage that's convenient to generate. A model that never saw a failure like that will improvise, and mine improvised a second R.

Then I made the stick

Back to the strip.

It isn't a training bug, it's physics. Every cell only knows its neighbours. A strip along the top contains zero information about where the G and the R used to be. The local rule found a different stable shape, a stick, and was happy with it.

I measured it on the shipped weights:

  • keep the top 30–40% and it never recovers;
  • lose 55% and it heals after about 2,000 steps, way after the loop used to stop at 700.

No amount of training fixes this. The information just isn't there anymore.

So the fix isn't a smarter neural network. It's ~280 lines of very boring TypeScript.

A dumb supervisor for a smart local system

The recovery guard watches the automaton from outside and compares each frame to a coarse fingerprint of "GR": how many cells are alive, and on an 8×8 grid, where they are and how green they are. A few dozen numbers, not the image.

Its rules are simple:

  • Less than 40% left? Don't pretend. Regrow from a single cell and tell the visitor honestly: "Couldn't heal this one — regrowing from a single cell."
  • Doesn't look like GR by step 380? Give up and regrow. Normal cuts and bites look grown by ~200, and hopeless cases are still nowhere close at 400.
  • Looks right but still changing? Give it 400 more steps instead of freezing it half-healed.
  • First grow came out crooked? That happens on about 6% of random seeds. It happens behind a static SVG, so it gets silently regrown and nobody ever sees it.

The thresholds aren't vibes. They're calibrated on ~400 grows plus heals, and they have to catch the old "RR" weights, which I keep in the repo as a regression fixture. A guard that can't catch a known-bad model doesn't ship.

Lesson two, and the one I actually use at work: a local model can't recover information that isn't there. Give it a dumb global supervisor that knows when it lost the plot and restarts it honestly.

That's a pod and a liveness probe. It's an AI agent and the deterministic check around it. Same shape, smaller logo.

What it costs the page

It's a portfolio, and nobody should pay for a party trick before they scroll to it.

  • The only eager code is a 0.47 KB loader that waits until the block is about to scroll into view.
  • Weights are float16 in a tiny binary format: 15.5 KB gzipped. The training script only overwrites the shipped weights if the new ones score better after the float16 round-trip, so a retrain can't quietly ship something worse.
  • Inference is ~240 lines of plain TypeScript in a Web Worker. No WebGL, no ML runtime, and it runs only while the block is visible.
  • Until the model loads, a 0.4 KB static SVG holds the place. With Save-Data, 2G or no JavaScript you only ever get the SVG.

It shipped at Lighthouse 1.0 on mobile and desktop, CLS 0.

Built by agents, judged by agents

I built this the same way I build production features now: a writer agent, then independent judge agents that try to break it, and every finding becomes a red test before it becomes a fix.

The judges found the "RR", a dead blank square after a full wipe, a blocked touch scroll on phones, a second timer loop that could start inside the first one, and training that could overwrite better weights. The tests run on the exact float16 weights the page ships, not on the training checkpoint.

Nobody caught the stick, though. That one took a human with a mouse trying to break his own logo.

It's still not perfect: over 90 seeds × 8 damage types, 11 of 720 heals miss the bar. Reset fixes them.

Four days later

I wrote this on the day the site launched. About fifty pull requests later, the guard hasn't changed, but the GR has more work.

  • It reads with you. On every post page, this one included, a small GR sits in the corner and grows from one cell as you read: a seed at a quarter, half a GR at the halfway mark, the whole logo at the end. Once grown, it heals like the big one, the guard included. Between 23:00 and 06:00 by your clock it sleeps.
  • It's on the 404. A wrong address gets a GR with a slash through both letters, and it heals while the page suggests where you probably meant to go.
  • It couldn't become the per-post creature. I wanted every post to grow its own variant of the logo. Then I checked the shipped model, and it grows the same GR from any seed: that's the one shape it was trained to return to. So the creatures on the post cards come from a plain 11×11 cellular automaton seeded by the post's address. No network, about 140 lines.
  • The simulations got their own CI. Growing, biting, cutting and regrowing on the shipped weights takes 30–45 seconds of a CPU core per test file, and next to the rest of the suite they started timing out. Now every file gets its own runner, and they also run nightly on main.

Code is public: github.com/Grumbash/rossev.dev. Training is in scripts/nca/train.py, and the model and the guard are in src/scripts/nca/.

Go break it: rossev.dev. If you manage to make it settle into something that isn't GR, I genuinely want to know how.

#Neural Cellular Automata#Self-Healing Systems
COPIED TO CLIPBOARD