ToonTalk 3D · a making-of
Thirty-two working days, 338 commits and one 29,000-line file — the day the project stopped guessing at ToonTalk and went and read it, and everything that followed from the day a picture became a pad: panels, games, puzzles, a yard, teams, a narrator, robots that train robots, Hilbert's resort in five problems; a third voice at the table, a tester that never slept — and, last, a hand you can tell what to do.
| Before media 16–24 Aug |
Since 25 Aug–20 Sep |
The hand 21–26 Sep |
Total | |
|---|---|---|---|---|
| Working days | 9 | 18 | 5 | 32 |
| Commits | 154 | 145 | 39 | 338 |
| Prompts from Ken | 174 | 232 | 47 | 453 |
| Tool calls | 5,491 | 13,558 | 894 | 19,943 |
| Lines in the app | 11,959 | +15,785 | +1,601 | 29,345 |
| Example worlds | 18 | +48 | — | 66 |
| Puzzles | — | +39 | — | 39 |
| Manual (lines) | 544 | +1,488 | +5 | 2,037 |
| Regression checks | — | +146 | +3 | 149 |
| The back of the book (lines) | 577 | +7,924 | +763 | 9,264 |
A prompt is a message Ken typed; a tool call
is one action taken in reply — a file read, an edit, a probe of the
running app, a Blender render, a test run. Thirty-two actions to the prompt
before media, fifty-eight in the second part: that is where measurement got
cheaper than guessing, and most of each round went into proving the fix
rather than making it. Nineteen in the week of the hand, where most of the
testing was done by a tester in another browser and came back as a report.
Both rows are counted from the session's own transcript (the whole project
is one session) with one rule for all three columns, recounted on
26 September; the rule gives the first column's tool calls exactly as they
were first counted, and six fewer of its prompts. The other rows are counted
from the repository, split at the end of 20 September; the lines in the app
are the copy the hand was built in, which is 1,517 lines longer than the
released workshop.
The back of the book is BACKS.md: one entry per round,
saying what was asked, what was measured and what changed.
Ken Kahn is writing about this project from the other side of the collaboration — his post is here. Every prompt of the conversation, in order, with what each one changed, is in the ledger.
ToonTalk is a programming language from 1995 that looks like a video game. You do not write code in it; you go into an animated world and show a robot what to do once, and it remembers. Numbers are blocks you type on. Messages are birds who fly things to their nests. A program is a house with somebody working inside.
ToonTalk 3D is a from-scratch reimagining of those ideas in real-time 3D: a single HTML file, three.js, and a cast modelled by Blender scripts rather than by hand. Nothing of the original's artwork is copied — the robot, the vacuum, the Martian are all new. What is being copied, and argued with, is the semantics.
This is the log of how it went, and of the moment it changed direction.
The first three days were built from ToonTalk's reputation — from what its ideas are famous for. That gets you further than it should. By the end of day three the workshop had numbers as exact fractions, text pads, boxes with holes, a copier, robots trained by demonstration with thought bubbles, teams that take turns, birds and nests with real message-passing, a notebook, and rooms that hold whole worlds inside them.
Numbers, robots, thought bubbles. The first rule arrives early and never changes: a robot's every action is relative to the box it was given, never to a place on the table.
A population. Miniature robots, teams, the copier as a machine rather than a wand, scales, Dusty the vacuum, birds and nests — the first real message sent.
Rooms and the notebook. Worlds inside boxes, with doors and glass walls. Then a performance wall, and a nagging feeling that the semantics were thinner than they looked.
It looked right. It was even fun. But asked to write a real program in it — one that branches, recurses, and chooses — it could not. Nobody could say precisely why.
On the third evening the work stopped and turned to two documents that
had been sitting there the whole time: the original ToonTalk manual
at toontalk.com, and the original C++ source. The output was not
code but a file called DIVERGENCE.md — every difference
found, each marked SAME,
KEEP (a deliberate improvement),
GAP (something real we lacked), or
FIX (a misunderstanding).
The gaps were not decoration. They were the reason no real program could be written.
That last one was the deepest. The whole concurrency model of the original reduces to a single method:
if (showing_on_a_screen()) { ... return the_default_duration; }
return 0; // off-screen: every animation takes 0 ms
Houses in ToonTalk are not separate documents. They are places in one city, every robot stepped from one list, and the ones you are not watching run at zero seconds per move. Our bottling was an accident of architecture pretending to be a design. Within a day it was gone: rooms became real objects hanging inside the scene at toy scale, and a single interpreter turns to whichever world needs a moment.
The measurement, from the audit file: recursive fib(10) = 55, across 109 nested houses, in under three seconds. The bottling engine had stalled for minutes on fib(6).
Semantics unlocks, current engine — DONE. Proven the same day: a sentence factory, doubly-recursive Fibonacci, and a message-passing bank account with a dozing teller. DIVERGENCE.md, 19 August
A manual tells you what a language offers. It does not tell you how anyone actually writes in it. The third source of truth arrived when Ken handed over five of his own ToonTalk files — swap, factorial, n-to-1, append, reverse — saved out of ToonTalk 3 itself. A .tt file turns out to be a zip with an XML document inside, so they could be read move by move.
They were full of idioms no manual would think to mention:
factorial takes a 1 from the stack and types a × on it. Our engine recorded that as "make it ×1" — an absolute value — so a robot taught on 30 would forever withdraw exactly 30. The fix was a new kind of remembered step: make it multiply, which works on whatever number the robot is handed.
All five of Ken's programs finish by fetching a bomb and blowing up their own house. We have no bombs. The replacement is quieter and, we think, better: a robot vacuums the very box it was working on. Nothing matches any more, so the team stops for good — and a house with nobody working in it and nothing to work on folds itself away. Recursive programs now clear up after themselves.
In the original, a scale lives in a box and weighs the holes
either side of it. Ours is a balance with two pans. Both of Ken's
scale programs port cleanly by moving the compared numbers into the
pans, and the difference is written down rather than papered over —
which is what DIVERGENCE.md is for.
The last day added Marty the Martian, an AI helper who lives in the workshop. He answers questions and — deliberately — never writes programs for you, since doing it yourself is the entire point. When showing beats telling he gives a demonstration, driving the workshop through exactly the same clicks a person makes: walking to the stack, fetching a number, carrying it to the table.
Marty was given a careful system prompt describing the workshop. And Marty, fluently and confidently, began to make things up. Asked how to make one third, he explained you type 1, then a slash, then 3. You cannot. Asked whether a number could be seen as a decimal, he said the workshop keeps everything exact and never shows decimals — while the number blocks in front of him were displaying decimal faces with a bar over the repeating part.
I wonder if Marty needs to be provided with a textual version of the manual to base its answers on. Ken, 20 August
Which is, of course, the same sentence the project itself had needed two days earlier. Marty is now handed the manual — the real one, this app's own, twenty-one thousand characters of it — with every single question, and told to say he is unsure rather than invent. The hallucinations stopped, because they were never really hallucinations: he was doing what the project had done for three days, describing a workshop he had never read about.
The last day went looking for somewhere to put the thing. A build script now packs the whole workshop — three.js, the loader, both Blender-made models as text — into a single 2.3 MB file that fetches nothing and runs from a disk, an email attachment or any static host. All eleven example worlds give byte-identical results in the packed build and the source, which is how it is checked.
A morning of probe pages went on finding out what a published Claude artifact will carry. That frame drops any inline script over about a megabyte without a word; it refuses to fetch anything at all, including a data: URL the page made itself. And then the wall: the workshop went white in it, markup and all, the moment its animation loop ran, at every boot delay tried. The conclusion written down that evening was that a live workshop is more than the frame will carry.
That conclusion was wrong, and it stood for three days. It came from one failing app and was generalised to the loop — the part of the sentence that was never tested on its own. On the seventh day a bare WebGL loop was published as an artifact and ran twenty thousand frames at a steady sixty a second, for six minutes, without dropping one. The packed 2.3 MB workshop was then published, and it runs. Whatever killed the first attempt was narrower than the loop and has still never been isolated — the honest record is that it is unknown, not that it is impossible.
The rest of the day was the kind of correction only the person using it can make. Marty walked under the table with his antennae through the top. He clicked Mimi the copier from across the room, so the number appeared on her platform by itself. Your notebook came up in your hand when you meant to turn a page, and Dusty would vacuum it away altogether. Undo did not reach inside it, because a notebook travels through a snapshot by reference — so undo handed back the same book with the page already gone.
And the notebook, which remembers everything between visits, now belongs to a name: the workshop asks who is working here, and two people sharing a browser no longer share a library. Marty, Mimi, Dusty and Ruby moved to stand along the sides of the desk, because the opening view is the first thing anybody sees and half of them were under it or past its edge.
Then Ken trained a robot to copy the box it was working on and join the copy back on. It is a perfectly ordinary thing to teach — it doubles a number, and it doubles the length of an array. He put it in a glass house and pulled the lever.
Twenty-odd rounds later the box had over 500,000 holes, and three separate layers had quietly given way, each in its own manner:
The box burst the walls. A house mounts its world at a fixed miniature scale — a constant shrink, which cannot cope with contents that grow without bound. The box punched through both walls and dragged the house off across the floor. Rooms now measure what is inside them and shrink further when outgrown, never magnifying a small one back up.
The camera moved once every second or two. Every hole was building a pocket mesh and an anchor, so half a million holes meant a million objects for three.js to walk on every frame — all of it geometry nobody could see. A long box now draws its first and last six holes with a ⋯ N more ⋯ plate between, exactly as a tall pile on a nest already did.
Saving it wrote 11 MB. Every empty hole was spelled out longhand. A box past sixty-four holes is now written sparsely: a hole count, and only the holes that hold something.
The measurements, on a 500,000-hole box holding a number at each end: 20 objects added to the scene rather than a million; 4.25 ms a frame against 4.61 ms for an empty workshop; the file down from 2.5 MB to 448 bytes, and its load from 1,916 ms to 334 ms. Both file shapes still read, so nothing saved before that day changed.
Updating the file format is fine. But for the future can we include version numbers in the files so changes won't break things. Ken, 21 August
Which is the request of somebody who has maintained a format before. Every world, thing, saved robot and notebook now records the format it was written in. An older file is migrated; a newer one is refused outright, naming both versions — rather than half-read into something that looks right and is quietly wrong, which is the failure the stamp exists to prevent. It cost nothing on the day it went in, and the sparse box was its first use.
The room fix landed the wrong way round to begin with: the budget was written in the room's outer units rather than its inner ones, and since a bare robot already measures about 1.2 across on that inside scale, the very first version clamped every room — robot included, nothing having grown at all. Caught by measuring a room that had no business shrinking, which is the sort of check that only gets written when you have already been fooled twice.
The seventh day opened on the one gap in the list above that is not a missing feature but a missing idea. ToonTalk has sensors — live pads that both read and write a picture's x position — and they are the single place the language allows two robots to interfere with the same thing. Read x, compute, write x: each write is atomic, and the pair still races. Worse, sensors are not user-definable. A child can build a message interface on top of one, but only the substrate decides what gets to look like a sensor, and the substrate speaks Cartesian.
The morning went on arguing rather than typing, and the answer was to delete the feature. Every thing can be flipped over; its back is a little tabletop where its behaviour lives, and flipping yields a bird addressed to it. Messages are ordinary boxes with a selector pad in front. A back handles one message at a time, so no-concurrent- side-effects holds by construction rather than by a special rule for sensors. A gauge stops being a kind of thing and becomes an idiom: an ordinary number with a robot on its back keeping it current, and another dozing on its event nest to push changes back. The system's own x and y sensors become gauges the system happened to mint, from the same parts anyone else gets.
One rule then swallows another. A robot whose match fails on a live thing dozes on that thing's events — which is exactly the covered-nest rule, generalised, so there is one rule where there were two. The lineage is ask-suspension from concurrent constraint programming, and the write-up says so, along with the disanalogy that matters: that store is monotonic and this one is not, so instead of confluence the design promises two smaller things it can actually keep.
None of it was built. It is BACKS.md, costed at six to
nine working days, in the order semantics-then-payoff. Writing the
design down and stopping is a choice this project had not made before.
Then the housekeeping: MIT, because Apache-2.0's patent grant buys nothing here; a link to Ken's own account of the project; and a pointer to the copy on GitHub Pages for anyone who only wants to use it. Ken refreshed the repository page and the licence was not there — the files had been written and never committed. A small lesson about what “done” means, learned in public.
Desktop Chrome carries a small Gemini inside it, and Marty can use it: no key, no account, nothing typed leaving the machine — which makes it the one brain a classroom can have without handing out a key. Two things had to be got right. Chrome will only begin the multi-gigabyte download from a real click, so the panel asks for it with a button of its own rather than setting it off from a child's first question. And the model is small: the manual is 22,500 characters and will not fit, so the manual's sections are scored against the question and only the nearest ones are sent.
Marty also stopped walking to an empty corner of the table before every demonstration — a trip that had been justified by a comment explaining why his props would otherwise land badly, when the code that places them never used his position in the first place. And he shows he is thinking: three dots circling over his antenna, because the little brain in Chrome is slow to wake the first time and a still Martian reads as a hung page.
Then Ken opened it on a tablet, and the whole interaction model came apart in a way no desktop testing would ever have shown. A finger is not a pointer: it is only anywhere at all while it is touching, and while it is touching it covers the very thing it is over. What you were carrying followed it, invisibly, to wherever you last tapped.
So on a touch screen the carried thing no longer follows. It waits at a fixed spot on the screen where it can always be seen. The first attempt parked it on the working plane, and the clamp that keeps a carried thing over the bench dragged it around until it sat squarely behind a panel; it is fixed in the camera's frame now, so orbiting turns it in your hand rather than carrying it off. Nothing on screen is a text field, so a hidden one gives the keyboard something to appear over, and what it receives is turned back into key presses and handed to the ordinary typing rules — one set of rules, not two.
That last part was wrong twice. A phone keyboard does not hand you letters; it hands you its opinion of the word so far, over and over, and revises it when it changes its mind. Reading each event as newly typed turned hello into hhehelhellhello. Nothing is read out of the events now: what changed in the field is worked out by comparison, so a prediction that rewrites a whole word arrives as the backspaces and letters it really is.
The tablet session ended with a list of seven, and two of them were not interface problems at all.
Ken trained a robot to double a number — copy the thing it was given, combine the copy with the original — and did it by parking the copy on a work spot and bringing the original over to it. Running it, he was told the robot had swept its own box away: finished, for good, and his answer was gone. Both halves were wrong. An empty stand had been read as a robot announcing it was done, when it had merely put its work down somewhere else; and the routine that clears the work spots between runs deleted what was on them. An empty stand now reclaims from the work spots first, and work spots are cleared onto the table. Nothing in this workshop should make a thing cease to exist except Dusty.
Then: a robot trained by typing “Ken ” on a blank pad turned every pad it ever met into “Ken ”, where Ken expected it to keep adding. The codebase already agreed with him in the next function along — there is a comment there explaining that an operation typed on a number is its own step, because a robot shown “make it minus” must not remember “make it −30”. Writing on a pad had exactly the same shape and the opposite treatment. It records the addition now, and the file format went to v3 so an older build refuses the file rather than replaying it wrongly.
The last report of the day was the most interesting, because nothing was broken. Take a result off the robot's stand, put it on the desk, press Undo — and it appears at the corner of the desk. Press Undo again and it returns to the stand. That is correct: putting something down is two moves, so undoing it is two, and the step between them is you holding it. But when you click Undo the pointer is on the Undo button, and the invisible hand draws what it carries at the pointer — so the thing was rendered low and near the front edge, which reads as “it wandered off” rather than “you are holding it”. It waits at the visible spot now, until the pointer next moves over the workshop, and says so. Being right is not the same as being legible.
The day ended somewhere else entirely: Ken asked for the seven Exploring Infinity activities from the WebAssembly ToonTalk — the counting numbers, the evens, the squares, a list that maps another list, one that filters, one that zips two together. Each is a robot that takes the head of an endless list, does one thing to it, and hangs the tail back on its own nest: a coroutine, written by demonstration.
Only one robot can stand at the open bench, so seven of them out in the room would have to be run a stage at a time. Each got a room of its own instead, and rooms all run at once. That immediately found a real bug. A room may run forty rounds in a single slice, and each round sends a bird — so forty couriers were in the air together, and they landed in whatever order their flights happened to end, which is to say backwards. The stacks came out 2, 40, 38, …, 4. Every bird now carries the number of the flight it was sent on and waits its turn at the nest; they land 2, 4, 6, …, 40.
The eighth day opened with a correction from the person who would know. A note had gone into the documentation saying that rooms only run at a quiet moment — while nobody is dragging anything and no robot out in the open is mid-step. Ken read it and said: that was not true of houses in the original ToonTalk. It was not, and the restriction had never been a design decision; it was a scheduling convenience that had been written up as though it were a rule. It came out. Houses run while you work, which is also what makes a switched-on house look switched on rather than stalled.
Then a puzzle that had been mis-solved twice. Published as an artifact, Marty could offer a brain that needs no key and no account — the messages endpoint answers, borrowing the reader's own claude.ai session. Except that it did not answer, and the app blamed the reader for being signed out. Ken pointed at Comic Chat, where the same trick works, and the difference turned out not to be the code at all: a chat artifact and a Claude Code artifact are two different frames. The keyless call lives in the first. The second has a window.claude too, but it is a capability kernel — downloads, storage — with no completion in it, which is exactly why testing for the object's presence gave the wrong answer. The route is chosen by the shape of what is on the page now, not by the fact that something is.
Which leaves a size problem, because the packed build is 2.3 MB and a chat artifact will not take it. The answer is Ken's own, from Comic Chat a month earlier: ask the reader for the assets. A third build, build_chat_artifact.py, writes a 500 KB file that takes three.js from a CDN the chat frame allows and asks whoever opens it to drop the two models in, once, and keeps them in that browser afterwards. Nothing inside the app knows the difference — the model loader already preferred a model it had over one it would have to fetch.
Then a batch from the infinity activities, all of them about being able to see. The instructions name the houses and the names were painted on the side, where you are not standing; they are on the front now, above the door, in a larger hand, and the walls grew to make room for them. Nest piles taller than a screen showed an ellipsis in the middle, which was honest but fixed — you can walk the window up and down the pile now and read any part of it.
And the request that turned into the day's real work: let the user turn what they are holding, to see the other faces of a number. Arrow keys, quarter turns, which is easy. What is not easy is that a cube turned by quarters lands the face you asked for on its side or on its head about half the time, and that is a poor reward for asking to read it. So after every turn the thing rolls about the line of sight to whichever quarter leaves the writing upright — found from the meshes themselves, since the six faces are real planes and the one facing you is the one whose normal points back at the camera. No table of orientations to get wrong.
Numbers had a blank face on the bottom, and Ken asked what might go there — continued fractions, perhaps? It was the right suggestion, and it took four rounds to draw properly. The list form [3; 7, 16] is exact and tells a beginner nothing; what a continued fraction is only shows in the staircase, each term with the whole of the rest tucked under its own bar. The first attempt drew the first term underneath the operation badge. The second ran off the right edge and silently lost a term. The third left the last denominator hanging off the side of its bar. Each was measured against the rendered face rather than argued about — a dump of which pixel rows have ink in them settles in one step what looking at a screenshot does not.
The other faces came out of the same batch: a decimal expansion that says how long it repeats (355/113 repeats after 112 digits, which is why it looked as though it did not), a prime factorisation that stops by work done rather than by size so it no longer goes blank on a big number, and a message for 1 that says 1 has no prime factors rather than the no primes at all that Ken thought a beginner would read as an accusation.
A robot occasionally worked at its sleeping size, and it had resisted two fixes across two days because both were aimed at the wrong world. The check that catches a robot left small ran after stepping out of the room it had been working in — out in the workshop, where the robots inside a house are not reachable. It runs inside now, and Ken's report was precise enough to say so: the robot in front was full size and the one in the house was not.
The ninth day divided cleanly into finishing, designing, and — for the first time with a written plan in hand — building.
The finishing: the nest-as-window promise from day eight turned out to cover only taking. Ken trained a robot the natural way — drop a ×2 straight onto the 1, never lifting it out — and handed it a nest: the ×2 was refused and the delivery went to the bird untouched. The put side now reads the same trainedOn signal the take side does. And the tiny robot, reported three times and half-fixed twice, was closed the way the first half had been: a flag stating what size the robot should be, set when a transition is decided rather than when its animation finishes, plus a registry of every robot ever made — because mid-transition a robot belongs to no list, exactly when an interruption is most likely to catch it. Reproduced stuck at scale 0.383, snapped back to 1. Dusty and Ruby also put a ring on the cursor — his warm red, her pink — because their dial changes what a click does, and that state lived on a button you had looked away from.
Then BACKS.md tripled. Ken questioned the file's own title — in 2D
every thing has an unseen reverse side; here a number spends all six
faces and the back of a house is an outside wall — and the answer is
a panel, a place glued to its thing, with every semantic in
the design surviving the rename word for word. The flatness problem
for pictures got surveyed (billboards, cards, floating panels,
decals) and answered from inside the workshop: a picture is the same
tablet a text pad already is. Sounds arrived made and remade — the
badge gesture generalised, ×2 for double speed, ×(−1) for backwards.
And the anima-gadgets of the Playground project were read from their
own sources: behave.tt decoded, the IOE
catalogue transcribed whole, and Miki's 2000 write-up found, with its
warning about copying "the entire purple rectangle" turned into a
design rule — a behaviour must be one thing, so the copier takes all
of it or none.
PLAN.md turned the design into six stages with acceptance criteria,
a decision table, a risk list, and a capstone: Pong, built only from
pads and library behaviours. The release got its protection the same
hour — toontalk-3d-v1.html, a byte-identical
snapshot that index.html pointed at until 20 September, so
the epic could proceed in the canonical file without touching anybody's
released app. (The root points at the workshop itself now; the
snapshot stays as history.) The session-bound regression harness became
tests/regress.html with pinned goldens:
nine worlds, byte-identical or FAIL.
And then panels stopped being a document. Every thing in the workshop can now carry one: press the gear on the holding card (or Ctrl+P) and a tray slides out — a room with no walls, run by the same scheduler that runs houses, closed by a brass knob. What you lay on it stays glued to the thing: saving carries it, filing carries it, Mimi copies it independently, and a tray whose thing is vacuumed folds away by itself. A trained robot laid on a number's panel ran there and stopped on its own condition; the save stamped itself v4 only because a panel was actually in it; and the nine goldens came through untouched. Stage 2 — the bird, the event nest, and the deepest semantics in the epic — is next.
The tenth day opened with a solved mystery. Ken pasted one console line — the tiny-robot trap firing with a stack trace — and the number in it, 0.24095, confessed everything: MINI × 1.004 is the hover wiggle's own arithmetic. The wiggle captures a thing's scale when the pointer arrives and restores it when the pointer leaves — so dropping something on a mini robot and never moving the pointer, which is every tablet tap, stamped the mini scale back onto the robot that had just grown to the desk. Three days of "random" was just "did the pointer sit still". The wiggle defers to the truth now, and the trap has not fired since.
Then the epic's deepest stage was built and finished in the day. A thing with a panel is live: its tray carries a bird addressed to the thing itself, and a badged number given to that bird is the operation applied whole — +10 is fetch-and-add in one indivisible message, which is why two depositor rooms hammering one balance cannot tear a deposit. [query | bird] flies the value out; [listen | bird] — Ken's design, replacing the event nest that first sat on every panel unasked — answers with an event nest, the way an event listener subscribes. An update that changes nothing is not an event; the back swallows it.
And the suspension rule generalised: a robot whose mismatch is pinned on a live thing dozes on its events instead of stopping red. The covered-nest rule turned out to be this rule in disguise, exactly as the design document had claimed on paper.
Ken's testing bent the build twice more in passing: the thought bubble had stopped being imagination — a live delivery made during training was real, and now shows and then rewinds — and a mismatched text pad had never once turned red, because the flag code skipped every multi-material mesh and a pad is exactly that.
The stage closed on its two promised demonstrations. The gauge: two live numbers, a Sync room and a Control room forwarding set-badges each way — drop +3 on either and both read 8, drop ×2 and both read 16, and the ring stops after one lap because of the echo rule, watched working. And the bank account rebuilt as the canary: the balance is a live number and the old teller robot is simply gone — deposit is a badge, reading is a query, watching is a listen. Two rooms deposited at once and the balance landed on exactly 250. The info notebook arrived the same hour: an ⓘ plaque on every tray hands over documentation in Miki's 1999 layout, where the examples are real things — the +10 was copied off page two, edited to +7, and delivered. Documentation that can be copied and run cannot rot.
Stage 3 was supposed to be about media, and the surprise was how little of it was about media. A picture is not a new kind of thing: it is a pad — the same tablet you write words on, with an image on its face. Because of that it copies, files into a notebook, sits in a box, is vacuumed away and is recognised by a robot’s thought without one line of any of that being written again. The proof is a world called naming: six robots whose thoughts hold six pictures, dispatching on which shape arrived, the same way account dispatches on words and grammar on numbers. Nobody wrote a lookup table, and nobody wrote an image comparison either — a thought that holds a pad already compared what the pad said, and the picture came along for the ride.
A sound needed a body, and took three tries to get one. Ken’s first note was that the speakers were too close in colour to the pedestals they stood on; the rose cabinet that answered it landed a shade from the pink box stack, which was the same complaint wearing a different hat. What finally worked was not a colour at all. A wooden box with a black baffle is what says “speaker” before any label does, and nothing else in the workshop is that: the boxes are pink, the houses cream, the pedestals charcoal, the table pale pine. The face carries a woofer with its dust cap, a tweeter, a brass-framed screen showing the waveform the sound holds, and the raised parts are placed by the same rule that paints the flat ones, so the cap sits in its own cone instead of beside it.
Two conventions arrived late and reached further than the features that prompted them. Picking a sound up used to play it, which Ken called startling — and the fix he proposed was ToonTalk’s own: space turns a thing on and “.” stops it, whatever is in your hand or under the pointer. It plays a sound; it sets a house or a panel’s robot working. One gesture, every kind. The second is smaller and just as useful: a notebook’s spine is a grip of its own. Click it and you pick up the book rather than what is lying on its pages — and it is the one hold Dusty accepts on a notebook that still has entries in it, which is how a notebook you had actually filled finally became something you could throw away.
The stage closed with two folders of examples rather than a feature list. examples/sounds/ runs tones (a sound arrives silent and is made by a [frequency, seconds, shape] box), transforms (×2 is an octave up, ×−1 plays it backwards — the same minus sign that makes a number subtract, applied to a thing whose axis happens to be time), and melody, where a robot that knows no notes sings a queue of numbers into a C major scale, a note a round. examples/images/ runs pictures, naming, and an album, which is a notebook with pictures on its pages and no album code anywhere. melody and naming joined the regression gate, which is now thirteen worlds and asserts on a tune and on what six robots said about seven pictures.
Ken asked for something that sounded like a permission and turned out to be an architecture: “while a full size robot is running the user should be able to do most everything”. The workshop had frozen for the length of every run since the first one, and the reason was a single variable. What you carry hangs off a hand node that follows the pointer; what a robot carries hangs off its grip — two different places in the scene, and one held between them. So the two could never be full at once, and freezing everything was the only safe rule.
Splitting them is the whole fix, and the seam was already there: every use of the claw sits inside the step functions, the step runner and the training recorder. Training is the one place the two blur, and it is worth naming — there YOU direct and the ROBOT performs, so what you are typing on is in its claw. After that the frozen gate became a question about where you clicked: the robot’s desk, its work spots, the copier and its thought are its own, and the rest of the table is yours. Measured: seven plus five made twelve on the table while a factorial ran, and the factorial still answered 120.
The naming world existed to show that the dispatch is the matching, and it showed none of it: six robots stood in a row, one silently turned out to be the one that fitted, and it walked up and worked. They try in turn now. Each robot in front of the winner steps up, its own thought appears over its head with the parts that do not fit picked out in red, it shakes its head and goes to the back of the queue. One rule came with the queue, and it is Ken’s: the first robot to doze stops the team until it wakes. A queue where a sleeper is stepped around is not a queue.
“Is there an invisible door near Marty — he’s jumping up and down and the tooltip indicates a door is there.” There was. The way out of a house is a door a metre and a half tall standing at the left of the floor, switched off while you are on the open table — and three.js stopped skipping invisible objects for raycasting years ago, so it had been solid to every click and every tooltip, right where Marty stands. One line: things you cannot see answer nothing.
Ken put a problem down with the answer he did not want attached: “Dusty can remove subpictures but it doesn’t have a way to spit out what it has sucked up. I’m reluctant to add a button on Dusty and have keyboard shortcuts but I can’t think of good alternatives.” The alternative turned out not to be a control. What he swallows rides on his head, at doll size, where you can see it — and the workshop already knows what to do with a thing you can see: you click it and it is in your hand. Nothing new to learn, nothing new on the screen, and the ToonTalk move Ken was after (take one piece out of a scene and look at it on its own) is two clicks. He keeps the last six; older ones fall out of the bag, because a vacuum that never lets go is a cupboard.
“Triangle isn’t running but is in the way of the robot who is to the right of him.” Two robots, the same size, at one desk. The layout was not helping, but underneath it was a real fault, and one the previous round had introduced: takeTheFloor decided everything the moment it was called — who is standing there, which slot the outgoing one goes to, where it starts from — and the new try-and-fail queue calls it five times in a row before any of them plays. So every swap in the round was computed against the same stale answer. It decides in start() now, when its own turn comes, and the ones waiting stand in a row well behind the desk at doll size.
Ken assumed the hearts in the album were subpictures, and reported that Dusty swallowed the whole card. He does not: pointed at a real subpicture he takes exactly that, and only that one wiggles — both already true, and both worth checking rather than assuming. The album’s hearts are not subpictures. They are ordinary pads with a picture on the face and the pad’s own writing across the foot, drawn on a band so it can be read against whatever is behind it. Which is also the answer to his last question, the one about how to make one: hold it and type. Nobody could be expected to guess that, so a picture says it now when you point at one.
One thing the wiggle really was getting wrong, found while checking: it rescaled uniformly, which stood a flat sub-pad back up into a slab every time the pointer crossed it.
The round before had ended with a good answer to the wrong question. Ken had asked how to make the grey band with white writing under the pictures in the album, and the answer — hold a picture and type — was true and unsatisfying. He said so: “this feels too special purpose. There should be a generic way to create the gray rectangle with white text that can be dropped on a picture pad.”
He is right, and the correction is a better feature than the thing it replaces. How a thing is painted is data. A pad and a number both answer [set | background | grey], [set | colour | white], [set | font | sans], and a pad also [set | width | 2] and [set | height | 1/3] — sent to the thing’s own bird, in the same grammar as [query | bird]. So a label is not a feature at all: it is four messages, and a robot shown those four messages can label every picture it meets. Each of them is a real box on a page of the thing’s info notebook, which is Miki’s 1999 rule again — documentation you can take out, edit and run cannot rot.
One thing had to change underneath. A pad’s face was drawn on a fixed 512 by 384 canvas, with every position in the drawing written as a number on that grid. A pad that can be two wide and a third high cannot use it: the writing comes out stretched. So the face is said in fractions of itself now, and the canvas follows the shape.
“When I used Dusty to vacuum a picture off a notebook I didn’t see the picture on top of him.” The bag worked; the notebook was the problem. Clearing a page re-renders the notebook a beat after the flight, and rendering a notebook throws away every node it drew last time — including, by then, the one sitting in Dusty’s bag. Forgetting that one node before re-rendering is the whole fix.
Stage 4 was down as “the Devices notebook” and turned out to be the shortest stage of the six, because almost all of it had already been built for other reasons. The workshop’s senses are nests: press Devices in the menu, take a keyboard nest off a page, and every key you press lands on it.
What makes that nearly free is a rule that has been in the workshop since the first list example — a robot facing a bare nest dozes. It was written to let a program wait for an answer that had not been computed yet. It turns out to be exactly what waiting for a key press is. So a robot handed a keyboard nest wakes once per key, waits in between, and needs no event loop, no callback and no subscription. The whole of keys.world.json is two steps: take the key off the nest, join it onto the pad. It types what you type.
The other property arrived by construction rather than by decree. A device has to be read-only — nothing you build should be able to write to the keyboard — and there is no rule enforcing that. A device nest simply arrives with no egg, and a bird is the only way anything is ever put on a nest. No bird can be had, so nothing can be sent. The guarantee is a consequence of the furniture.
One thing had to give way. A lone eggless nest lifted off a notebook page is replaced with a brand new one — which is what makes the blank-nest page useful, and which was quietly costing a device nest the guid that makes it a device at all. Devices say what they are, and the rule steps around them.
The Playground project shipped ToonTalk’s best answer to how does a child reuse behaviour without reading code: anima-gadgets, a picture whose front carried behaviours as purple rectangles. You took one, flipped your butterfly over, and put it on the butterfly’s back. The butterfly was ready.
Reproducing that needed one thing first. A behaviour moves things, so where a thing is had to be sayable — and it joins the appearance API in the same grammar, on the same bird: [set | across | 1/2], [move | position | [dx|dz]], [query | position | bird], and three channels to doze on: position, edge, and touch, which hands you a live bird to whatever has just run into you.
Then a behaviour is a pad whose panel carries robots that talk about my thing through a live bird, and binding is not editing. Drop the gadget on a butterfly and exactly one kind of thing changes: which thing that bird is addressed to. Nothing inside is rewritten — not a step, not a thought. The bouncing gadget reports eight references re-pointed and nothing else touched.
Self-demonstration then costs nothing, and that is the part worth pausing on. It is not a demo mode: an unbound gadget’s bird points at the gadget itself, because my thing means itself until somebody says otherwise. Set one down, press space, and it does its thing to itself. A shelf of gadgets each doing its own thing is a catalogue you read by watching, which is exactly what the 1999 write-up described.
The obvious design for bouncing is a robot that moves and another that watches for an edge. It cannot be written. A team is tried in order and the first member to doze stops the team — Ken’s own rule, from two rounds ago — so a watcher facing an empty nest would stop the mover for ever. So the edge is not an event: it is a reading, one pad saying which edge the thing is against or “none”, never empty. And then the bouncer is three robots that differ only in the word they expect, none of which can doze.
The acceptance ran with no training anywhere: a star on the table, the bouncing gadget dropped on it, space. Out to 1.24, turn, back to −1.17, turn again.
“Can you try to make the pong game.” The plan had said the capstone should be built only out of pads and library behaviours — no bespoke robots — and that clause turned out to be protecting the right thing for a slightly wrong reason. Four of the gadgets Pong needs are among the six of twelve still unbuilt (one of them is literally send a message to the score), so the ball and the bat carry their own robots on ordinary panels instead. What survives, and is the claim worth making, is that no engine work was done for Pong.
The table is the court. Three of its walls are the table’s own edges; the fourth, on the right, is yours. The ball, the bat and the counter are three ordinary things standing on the table, and the ball and the bat each carry their own program on their own panel — so two programs run at once, which is the difference between a game and a demonstration.
The ball’s team is six robots that differ only in what they expect in two holes, the edge and what it is touching: far or near wall, flip the away step; left wall, flip the across step; right wall, give the counter’s bird a +1 and start again in the middle; touching a thing, flip the across step; anything else, fly. Flipping is a ×−1 dropped on a number, exactly as you would drop one by hand. Every part of it was built for something else, weeks apart and for other reasons: [move | across | n] and the edge reading from the behaviours, the pointer device from the senses, a badged number given to a bird from live things, and [set | width | n] from the appearance API — which is the whole of why the bat is a bat and the ball is a ball. Nothing anywhere knows the game is Pong.
Three bugs stood in the way, and all three were found by measuring rather than by thinking about it. Touch had been an event, and events do not work here for the reason the edge already had written on it: an event leaves the nest empty, an empty nest makes a team member doze, and a dozing member stops the member doing the moving. A ball that bounces off a bat could not be written at all. It is a reading now — a live bird to whatever is against you, or a pad saying nothing — swept every frame, because a behaviour takes a round every frame and a ball told twice over that it has just hit the bat turns round twice over, in front of the bat.
Auto-run belongs to the table. Mail landing in the standing robot’s own box arms another round, which on the table is exactly right: a robot dozing over a mismatch should get to work when the post arrives. Inside a panel it is a catastrophe, because a behaviour whose work box holds a reading gets mail every round — and the extra round ran from inside the first round’s own queue, outside the scheduler, outside the Rounds limit and outside the frame budget. Measured: 594 extra rounds in a single frame, sixteen seconds of one frame. Which is precisely what Ken had been reporting for two rounds as “it freezes for about a second, even Marty stops”.
And the edge had been clamping with a strict greater-than, so only the move that was refused said “right”. A round sends two messages — the across step and the away step — and the second asks for a position the first already clamped to, so it is not refused, so it says “none”. The reading was back to none before the round that would have acted on it began: a robot whose thought asked for the right-hand wall never once saw it. The edge says where a thing is now, not what was refused.
Verified by driving the frame clock and the pointer together. A player who tracks the ball saves it twice and misses nothing. A player who walks away is past at frame 675 — the counter goes to 1 and the ball starts again in the middle of the table.
Media was supposed to be a stage of its own. It turned out to be a lever: almost nothing in it was about pictures or sounds, and almost everything it touched came back more general than it went in.
A picture is a pad, so it copied, filed, matched and travelled the day it arrived. A sound needed a body, which is a wooden box with a black baffle, because nothing else in the workshop is wooden. Then the questions started arriving that had nothing to do with media at all.
The cost of all this is measured in the table at the top: the second half of the project is a third of the commits and two-thirds of the tool calls. Media did not make the app bigger so much as it made every existing part answer a question it had never been asked.
Marty the Martian was a signpost: a character who said where things were. He can now be asked. A key you paste stays in your own browser and is never sent anywhere but to the model you chose; desktop Chrome carries a small Gemini with no key at all, and that is the default. What he does with an answer is the interesting part. Make turns a name into a parts list — a boat is a hull, a cabin and a mast — and the parts list is built as a model, an ordinary thing that copies, files and can be shown to a robot. Draw answers with turtle steps, which the workshop already knew how to walk. Neither adds a capability: both write down something the workshop could already hold.
The original taught itself to a child through 74 puzzles, and Ken's rule for bringing them back was that a puzzle must not be a new kind of thing. It is not: a puzzle is a world file with a few more fields — what Marty says, the goal in words, hints in order, which stacks and tools the table keeps. The goal stands on the table marked fixed, so it can be looked at and copied but never taken. You answer by giving a bird your box, and the judge is a robot team in an opaque house whose thought bubble does the judging: a box matched hole by hole, in order, a number with its badge. Nothing judges; something recognises.
Thirty-four of them exist now, and the last few are about robots: one moves stuck numbers into a second box in reverse; one is that same robot, already trained and too fussy, so Ruby is the whole puzzle; one counts the letters arriving on a nest and dozes when the nest is empty; two grow a number or a box for ever and have to be stopped in time, because there is no rounds control — a robot runs until its thought stops fitting. The last two are teams: one where two robots take a single run between them, and one where four sort three numbers by which of them recognises the case in front of it.
Two small ideas came out of it that the workshop kept. Things can be stuck: your fingers cannot shift them and a robot's claw can, which is what makes training one worth the trouble rather than moving the numbers by hand. And Dusty swallows a stack: point him at the box stack and it leaves the world, rule and all, which is how a puzzle comes to have fewer tools without anybody editing a file.
Mimi got a face while this was going on. Her name says what she is — a mime — and a mime copies what she is shown. She stands at her machine, reaches up to the thing on the platform, follows the scanning bar down with her hands, gathers the copy out of the air, lowers it into the tray, and hands over whatever is taken off her platform. The animation is driven by the copier's own steps, so she keeps time with the machine at any speed.
The smart camera arrived at the end of the first fortnight as one rule: come in on what the robot is working on, and let go the moment the player takes it. The days after gave it occasions. After a lesson it pulls back far enough to hold the robot and the thought it has just made. When red parts appear in a thought — the marks that say why a robot will not run — it shows them. Waking Ruby or Dusty brings it in close on the thought, where their work is about to happen. And when a message names Ruby, Mimi, a stack, the notebook or the judge, it pulls back until that is in the picture as well as whatever you were looking at, because a workshop that tells a child to go and wake somebody ought to show her where she is.
None of it moves while your mouse is moving — only a moment after it rests — and your own orbiting always wins: the follow lets go until the next occasion. Thought bubbles became clouds in the same round, which is a smaller thing, except that a cloud has an outline a child can point at.
One report was worth a day on its own: a workshop that opened to a blank page where a library had been. The notebook had been written by a newer build than the one opening it, and the old build, unable to read the page, was ready to write an empty notebook over it. A notebook this browser cannot read is now left exactly where it is — the pages are still there for a newer build — and ?fresh opens a workshop with a clean library without touching the stored one. A data loss that never happened is the cheapest bug there is; this one was one prompt from being permanent.
Behind every place — the workshop, and every house — is open ground. The back door stands beside the tall door out, and through it your table stays indoors and the ground takes its place: ten metres by five and a half, grass underfoot, at floor level. Your hand, the stacks, the robot's desk and Mimi all come along, exactly as they do into a house. So whatever a robot sweeps off its work spots at the end of a run lands on the grass, and so does whatever Marty makes — a village from a house‑maker, a zoo from a Make.
Not a room. A room is a copy of the workshop, with a desk and stacks: the wrong furniture for a zoo. And your table came with you is the app's one rule about places, so the yard keeps the rule by swapping the table for the ground rather than inventing a third kind of place. A yard entered from inside a house belongs to that house, and is saved and folded away with it.
Doors wear signs saying where they lead — Back to the workshop, Back to “Add 1” — and pointing at a door and typing changes what it says, because a place you made should be able to say its own name.
On a touch screen he forgot he was holding something, reached for it where it sat, and turned the camera instead. A finger that lands on the thing in your hand now carries it, and the thing goes where the finger lifts. And he asked what had happened to the crashed rocket. It had been on its side, nose bent, a fin snapped off, smoking. It stands on its fins now, leaning seven degrees on the one that took the landing, and Marty says he landed badly.
A behaviour is a panel: a little world with robots in it, bound to the thing it rides on. This was the day the panel stopped being a leaf. You can go into a panel from the yard and come back out. A panel can be dropped into a box hole, and the crocodile wearing it goes on wandering inside the hole. A panel can be set down on the floor of another panel. A robot can take a panel out of a thing and hand it on, because a panel is a thing like any other.
A scene got a referee. Two things that run into each other need somebody to say so, and nothing in the workshop was going to notice on its own. A scene's own nest reports who ran into whom, and a robot waits on that nest exactly as it waits on a keyboard. Space Invaders reads from the back for the same reason: the game is a scene, and the scene says what happened rather than the game knowing.
Marty learned a second vocabulary the same day. Computing words puts the programmer's word beside the workshop's the first time each one comes up — a thought (a condition), a bird (a message), a house (a process) — for the visitor who already speaks one of the two languages and is trying to learn the other.
A team is ToonTalk's case analysis, and it is also its loop. One robot leads and the members stand by it; each pass, the first member whose thought matches what is on the desk takes the floor, and the ones in front of it step up, show why they cannot help, and go to the back. Nothing chooses: the choosing is the matching, seen. Sorting three numbers is four robots who differ only in which case each recognizes. Take a member out of a team and you are holding a robot again.
Every robot now carries a note: what it is for, in your own words, written on the card under its name. The note goes with the robot into a saved world, a saved thing and every copy; Marty reads it back when you ask what a robot does; the tooltip over a robot carries it. Every robot in the examples has one, so picking up a stranger tells you what it does before you read a single step.
The notebook stopped being furniture bolted to the table. Pick it up, resize it in your hand, carry it out to the yard, hand it to Dusty for a while; it remembers where you left it and how big it was, along with its pages. And Marty began talking through a run: a line before every step, with the robot waiting until the line has been heard.
ToonTalk Reborn's first tour has a robot pick up a fresh robot, give it a number, start its training, make two gestures, stop, and name what it has made. Ken asked whether that was possible here, and whether the robot's API would have to be extended for it. It did not.
In a lesson, the claw takes a box and drops it on a little robot among the robot's own things. That is the step teach: the little robot steps up to a desk of its own beside the teacher with the box on it, and everything done next is the pupil's lesson — recorded into the pupil as its own steps, and into the teacher as taught steps carrying them, Ruby's loosening of the pupil's thought included. Stop teaching it ends the pupil's lesson; the teacher's own goes on. Run it and the teacher does all of that for real: the pupil ends the run trained, standing at its own desk with its box, and counts when you start it. A robot may teach a robot; a robot that teaches a robot may not be taught to teach one. Two levels is where it stops being explainable, so two levels is where it stops.
Pads speak, which is how the teacher announces the lesson at the beginning and the end: [speak] given to a pad's bird reads its words aloud, and in a lesson Read is a step like any other.
The last rounds were about the watching rather than the work. Marty's opening comment — what the robot is trying to achieve — took seconds to arrive and longer to read out, and the robot stood still through every second of it; both asks now go at once and the opening is read while it works, with a line he cannot say at that moment left unsaid rather than queued behind. The camera had been sitting level with the bench, twenty-four degrees above the work, where the near wall of a box hides what is in it; it watches a run from forty-six now, and close enough that the box being worked on is a good part of the picture however wide the screen. And reading Marty's card at the side of the screen used to lean the camera all the way out, because the pointer had left the picture and leaving the picture means “let me see the whole table”. While a robot runs, it does not: watching is not reaching.
The working rhythm never varied. Ken plays with the workshop and sends a batch of observations — often with screenshots, usually five or six at a time, ranging from "the bird flew to the wrong room" to "eggs should be robin's-egg blue". Each one gets fixed, verified, and committed with a message that says what was actually wrong.
Verification is not by eye. The app exposes a development hook that can drive it headlessly — load a world, hand a box to a robot, run at instant speed, read the result — so every example is re-run after every change: swap gives 3/7, 5! = 120, reverse turns [1,2,3] into [3,2,1] across three houses that then fold themselves away.
The suite grew with the app: fifty-four checks at the end of the first fortnight, ninety-eight now. Each is a small story the workshop has to go on telling — a robot that teaches a robot, a ticker folded inside a thing that still keeps time, every face of a turned number cube reading the right way up, a camera that follows while a card is being read. They run in a headless browser against the real app and write a verdict file, and a check that goes red in company and green on its own is an order flake rather than a regression — which is worth knowing before blaming the change in hand.
Twice the verification lied, and both times in the same way — the measurement was wrong, not the code. Chasing why Dusty the vacuum was invisible during a robot's clean-up, a cached index into the scene's object list went stale, "proving" he never moved when he had. In another run, Dusty had been left awake from an earlier probe, so a test click vacuumed the very thing it was meant to pick up. Both wasted more time than the bugs did.
The real answer, when it came, was better than the bug report: Dusty was flying — at floor level, straight under the table, arriving hidden behind it. He goes over the top now.
Most of this workshop is the original, read carefully: numbers as blocks, boxes with holes, birds and nests, robots that learn by being shown, teams, houses that fold when their work is done, exact rational arithmetic that never rounds. The audit file keeps that honest. But a remake made thirty years later, in three dimensions, by two people who could ask each other anything, was never going to stop at a copy. These are the things a ToonTalk veteran will not recognise.
And the departures kept on purpose, from the day the manual was read: Dusty removes and Ruby erases where the original had one character with modes; the copier is a machine on the floor so a robot can use it as you do; mod and set are words. Each is a row in the audit file, argued for.
In the third week Ken brought a second model to the table. He gave ChatGPT the published workshop and asked it to play, and it played the way a good tester does: everything, in order, writing down what it saw. Its reports came back as folders — a Markdown write-up, the scripts it had driven the page with, screenshots, the world files it had built — and Ken triaged them line by line: ignore 1, investigate 2, 3 would be very nice, let’s discuss 5. Then the rounds went on as before, with one more source of observations that never got tired.
It wrote before it tested. Three small drawing lessons in the workshop’s own file format — a step-and-turn planet, an easy flower, a jumping spark — and three larger ones, a garden of three flowers drawn by a repeat inside a repeat inside a repeat, a planetary clockwork on sine and cosine badges, a firework fountain under gravity; it ran each one live, counted the rounds, and wrote a README in the voice of somebody who had read ours. They were moved into the examples’ own vocabulary, fractions in lowest terms and invisible ink where a flower would otherwise fall into petals, and they are in the gallery now with its name on them.
Then it audited. All thirty-nine puzzles, one by one, with a note on each; every example world; the gallery, twice — once reading, once running all fifty-seven cards and saying for each whether it had seen the promised result or merely a start. It found what a fresh pair of eyes finds: the album’s card describing a robot the world had not got, a reset button described as being on the wrong side of the screen, an introduction said twice after a reset, a letter given to a bird who was away landing in the room behind her, a teacher whose finish read as a failure, the title card hiding the back door at 800×450, a bank that would not answer until somebody started the house by hand. Each one is a fix in the back of the book, and most have a regression check with its name on it.
Its performance audit was the round that changed the most code. With a headless browser and a stopwatch it measured a single counting robot in a solid house at 87 to 130 milliseconds a frame, found that half of it was five 256×256 canvases painted for every number nobody would see, and showed the same counter fourteen times faster with them deferred; it put fifty counters on the table and reported that forty of them never ran. Numbers made where nobody can see them are data until they are seen now, room work shares one deadline per frame, and houses are visited from a rotating start — three changes it named, with a check that twelve solid counters all get their turns.
It was wrong sometimes, and said so first. A save during a bird’s flight it first called data loss and then, on a second look, a serializer limit behind a guard that prevents the loss; an “Instant makes the page unresponsive” that a later run could not reproduce. Its smoke test for the resort looked for a pad by a label where a pad keeps its text, and its little file server refused every request over a slash. None of that mattered much. What mattered was the shape of the collaboration by the end: one model builds and measures, another plays and reports, and the person at the table decides — which is, come to think of it, what a workshop is for.
On 21 September Ken pointed the tester at the narrator. ChatGPT opened the workshop in a browser, gave Marty an OpenAI key, and asked him the questions a newcomer asks — what a robot is, what happens when you type a 2 onto a 1, whether he could build something with you — and then did the same on the published claude.ai artifact, where Marty answers on the reader’s own account, and tabled the two side by side. Ken’s reading of the report was that some of the shortcomings were the model’s, but the first finding was ours: the prompt still described a Ruby whose erasing set the robot running, and Ruby had stopped doing that weeks before.
The two worst failures were the app’s, not the model’s. Marty’s robot demo said off it goes — 4, 5, 6 over a desk that stayed at 3: its script was written when loosening a thought bubble started a run, and had not caught up. It presses Run itself now, waits for the run to end, and its last line reads the desk rather than promising it. And in the building mode, asked to do the next step, Marty wrote I put a little robot on the table for us — and none appeared, because a reply had no way to touch the workshop at all. It has one way now: a reply may name a stack, and Marty walks to it and sets one fresh thing on the table with his own hands. The prompt says that is the only thing he can do himself, and that he may claim it only in the reply that does it. A check for each — the desk ends at 6; nothing is placed in the hints mode whatever the brain says — sits in the suite beside the earlier ones.
The rest was smaller and just as real. Typing digits appends — a fresh 1 typed 2 reads 12 — and the prompt had said it replaced. A robot whose thought fits keeps going, so “add one to any number” counts on and on; the prompt had implied a single pass. A stale Show me offer stayed under later answers about something else. The provider selector showed Claude while its label said OpenAI — the browser restoring an old form choice after the script had run. And ChatGPT’s accessibility tree could read the key box, which is a text field with the characters hidden because a password field would wake the password manager, which Ken had ruled out; that one stays, and is written down.
Ken also settled the defaults from the report — gpt-5.6-luna for OpenAI, gemini-3.8-flash for Google — and asked how they could be changed after a release without a rebuild. They can now: a served page reads marty-models.json from the site at boot and takes its models unless the visitor has typed their own. The round is in the back of the book.
It began as a question about voice control: could someone say “pick up a 1 and put it in a 3-hole box” and have it happen? Ken’s second thought reshaped it. Not Marty doing things on your behalf, but another way of moving your own hand; and not a grammar of commands, but a language model deciding what “a 1” and “the 1” mean from what is on the table — a fresh one off the stack, or the one already there. It would exist only where a brain did, and the release would not be touched: the work went into a copy, toontalk-3d-next.html.
The design fits in three sentences. The page writes the brain a numbered list of what is here — #1: a box with 2 holes (hole 1: the number 3) [left, front] — and what the hand holds, and where the hand is: at the table, out in the yard, in a panel, or inside a robot’s thought bubble, where the hand is the robot’s own claw. The brain answers with a plan in the workshop’s own verbs — take, type, drop, give, copy, vacuum, erase, leave. And the hand carries the plan out through the very clicks and key presses a person would make, so undo, a puzzle’s rules and a lesson being taught all see ordinary gestures, and every step is checked before the next is tried.
The rounds ran the way the rest of the project had, with one difference: most of the testing happened somewhere else. Claude, in Ken’s Chrome, worked through a written script, TESTING-HAND-BY-WORD.md, against whichever brain Ken had chosen — typing the sentences, logging what the brain was sent and what came back, and never seeing a key, which Ken always entered himself. Each report came back as a document, and each became a round. GPT managed 42 of 46 at first, then 61 of 66. Gemini Nano, the little model inside Chrome, managed 1 of 25. Claude, keyless in the published artifact, 22 of 29 on a build a round behind. And on 25 September gpt-6-sol took the scorecard 30 for 30, with nearly everything else right too, no errors of the hand’s own, and a median of two seconds a sentence.
Most of what the reports found was not a brain being wrong but the page letting a wrong thing look right. A plan that failed at step seven left six steps done — a stray 3 on the table, and inside a thought bubble half a lesson, which a robot then ran for thirty-three rounds. A failed plan is now undone, lesson and all, with the workshop’s own undo. A number dropped on a number adds, and the brain’s words hid it (“the original is back on the desk”, over a desk reading 2), so the answer now ends with what the drop made. A brain that did the possible half of a sentence and dropped the rest had its tick taken away: all or nothing. A plain “leave” answered with the lesson is over, and no steps, is no longer a tick either — only a question may be answered in words. And the list itself grew what the brain kept tripping on: which 1 is Mimi’s copy and which the original, which of two boxes is the leftmost, what is riding on what.
Ken tried it by voice, and the voice found its own bugs. A claude.ai artifact is never given the microphone, so there a click on it says how to start the computer’s own dictation, and puts the cursor where the words should go. Windows voice typing worked once and then stopped: the line was disabled while the hand worked, and a disabled field loses the cursor. Voice Access typed press Enter into the line as words, so “stop press Enter” was not a stop, and the robot ran on. And the page’s own microphone heard the page: Ken had not said “a room on the table” — the app had, aloud. It is deaf now while the page speaks. For someone using voice alone, the list says where each thing is on the screen, the camera turns and zooms by words, answers are spoken back, and a running robot can be sped up, paused or stopped by words while nothing else touches it.
The last question was whether something bigger than Nano, run on the computer’s own graphics card and downloaded once, would do. The workshop can now run an open model through WebLLM, and a scorecard, tests/hand-scorecard.js, judges any brain by what ends up on the table rather than by what it says. Qwen 3.5 with 4 billion parameters got 23 of 38; with 9 billion, 21. Keeping one running conversation, so the model reads its rules once, took a sentence from twenty-eight seconds to five. Letting the model think first was tried twice and taken back — free, it thought for four minutes and never reached a plan; told to be brief, it scored lower and took longer. The option stays, marked experimental, with its numbers shown before anyone downloads gigabytes. Every round is in the back of the book.
Every one of those lines is a row in
DIVERGENCE.md, which is still the file the project argues
with. It is the closest thing this workshop has to a conscience: a
written record of where it departs from the thing it admires, and
why.