My slides are a TUI app now

I’ve got two talks this fall: a 20-minute one, New Incident, Who Dis?, at SREday San Francisco this Friday, October 2nd, and a 50-minute one, They Don’t Stop to Think If They Should, at the WMU Cybersecurity Summit in Kalamazoo on November 12th. To keep things fun, neither has a Keynote file. Both decks are Go programs that run in a terminal.

I built both with Claude over about two weeks, starting with the long one, and it snowballed. There’s a framework now, and at one point it took four different models to draw one cartoon worm on a sled. I’m embargoing what’s actually in the WMU talk until November, but how it got built is fair game.

“Life, uh, finds a way.” — Ian Malcolm, Jurassic Park (1993)

Why a terminal

Mostly because it sounded fun tbh. I wanted to see how far a terminal could go: big block letters, pixel art, code-drawn animations, and the deck in a repo. Being a program also means I get tests, a linter, diffs, and SSH.

The SSH part is petty. A buddy of mine hosted his own TUI presentation over telnet for the retro points, and I couldn’t let that go unanswered. I answered his telnet server with SSH. Opus 5.5 put the WMU deck behind an SSH server: connect with any name, no password or key, and arrow through your own copy. There’s no shell behind it, and nobody else’s keypresses move your slide. I’ll share the address after November 12th.

The prompt did most of the work

The first deck started as a build prompt in my notes vault, next to the talk’s research. The note said: paste this into a fresh session in an empty repo, and copy my research first. My research is a large evidence dossier, the talk outline, and for fun the full text of Bill Gates’s 2002 Trustworthy Computing memo.

Friends asked what prompts I used, so I looked at what carried the most weight. Here is the main part of the prompt, straight from the file:

The files in `content/source/` are the only source of truth for every

number, date, quotation and attribution. [...] Do not invent, round, or

"improve" any figure.

Read `content/source/` first. Produce `PLAN.md`: the slide list with act,

budget, layout, set-piece id and the source footer for each; a short

design note per set piece; the library versions you intend to pin and

why. Stop and wait for review before writing the app.

Every animation has an explicit end state that is a good static slide,

and every animation can be skipped with `.` or by advancing.

Ask before deciding anything that changes what the audience sees:

palette choices, glyph sets, animation durations, and any wording on a

slide. When in doubt about a number, the dossier wins, and if the

dossier says "do not state," do not state it.The big one was making my research the only source of truth. Every slide with a number has to carry a source footer. My peers will be in the room. One fake stat wastes their time and ruins my credibility.

I also asked for a linter for the content, not just the code, which I described in the prompt as “the thing that stops a late-night edit from putting an unsourced number on the screen.” It grew rules I hadn’t thought of, like a density check that caps a slide at nine lines, because a font big enough for the back row doesn’t leave you many.

The one I’d tell anyone to steal is making it write a plan and stop. Step one was writing a PLAN.md with every slide’s budget, layout, and source, then waiting for me. I can read a plan in ten minutes. Unwinding thirteen thousand lines of Go I didn’t want would have eaten my weekend.

The rest was guardrails: refuse to run in a terminal that’s too small, have a --safe mode that’s plain ASCII with no animation, export a PDF backup, and never, ever print a crash onto the projector.

A week later, when I wrote the SREday prompt, I’d learned a few things. I was able to borrow from prior art: “Reuse the engine; replace the content.” I set the priorities: the whole talk as plain text slides with block text for flow, and only then any animation. If this went sideways, I had an outline and Apple Keynote so I could make slides by hand.

That deck now has pixel critters on most slides, a wizard running a tabletop game next to a sleeping dragon, and a full-screen home run from The Natural. In my defense, the content came first, and with time to spare I also had the PDF, since the conference asked for a backup of my slides (standard practice). The whimsy came after.

Oops, it’s a framework

I never asked for a framework. At first, anyway. I asked for a deck, twice.

The first engine, membrane, already came out shaped like one, because the prompt said to treat the deck as data and not code. That made refinements and updates easy. Slides are Markdown files with a bit of YAML metadata up top, a deck.yaml sets the order and how much time each section gets, and the speaker notes live right in the slide. It’s like how Obsidian or Hugo treats content. Here’s a real one from the SREday deck, notes trimmed:

---

id: inside-engineering

act: 0

budget: 45

layout: statement

steps: 3

sev: minor

pixels: outside

notes: |

SRE already solved incident response. ...

---

# We kept it inside engineering

severities · a commander · a working channel · a timeline · a blameless postmortem

+++

- Security: its own process, less battle-tested

- IT: a ticket queue

- Marketing: a group text

+++

**The team that says no → the team everyone calls.**+++ is a step break, and a # line gets drawn in big block letters. sev: feeds a little incident-channel status line at the top of every slide, so the talk runs its own fake incident. It sits at SEV Watch on the cold open, goes Critical on the CrowdStrike slide, calms down as the talk builds up the process, and ends Resolved. pixels: asks for a small animated scene under the text.

Under the hood it’s a Bubble Tea app with a timer that knows how long each section should take (and turns amber when I run long), tests that snapshot every slide, hot reload while I edit, and PDF export. The PDF was a fun detour. Charm’s freeze tool mangled the output, so Claude ended up writing its own exporter that reads the exact characters and colors the projector would get and draws those into the PDF.

For SREday I had it copy the engine over and make that untouched copy the first commit, so every diff after it was just the new talk. That took minutes. Then I had two copies of the same engine slowly drifting apart, which is how you end up with a framework whether you meant to or not.

Friends told me to let the agent build a framework. In the end, Opus 5.5 pulled the shared bits out into their own module, lectern, and it’s grown way past a cleanup. At some point I’ll release it on GitHub.

From Steamboat Willie to Spider-Verse

This is the part that surprised me most. Over those two weeks I went from Opus 5 to Fable 5.1 to Opus 5.5, with the same repo and the same kinds of asks, and the animations got noticeably better with each one.

Opus 5 got the bones right: the skeleton, the block letters, and a few set pieces that are still some of my favorites, like a line of text that rewrites itself on screen where only the letters that change actually move. But almost everything that moved was text. The logos on my bio slide were static shapes built out of circles and polygons, and the title card’s dinosaur was a flat silhouette.

Astra, OpenAI’s model, snuck in around here too. I handed it the logos in Codex, and its first pass had every single one assembled with the same set of ribbon, ripple, and shear effects. I told it they all moved the same, and it gave each one its own thing. A flag flutters, eyes blink, and the dinosaur strides.

Fable 5.1 is where the motion started doing the explaining. Things spring into place with a little overshoot, and diagrams animate the idea instead of just sitting there, like a packet running around a wiring diagram, lighting up each box until a wire gets cut and the packet bounces off the dead end. It also started testing animations mid-motion instead of only checking the last frame.

Opus 5.5 is where it felt like it had actually played the games. The old dinosaur read as a duck. It got rebuilt as a T-rex made of parts with a real walk cycle: knees forward, a bob twice a stride, the tail swaying, and two pixels of headroom so the bob doesn’t clip its head. There’s a test for that now too.

Then it went to town on the SREday deck. Every department got a pixel critter in its severity color, and they blink, hop, and act out each slide under the text. On “Build it and they will come*” they walk out of a cornfield onto a ball field, where the on-call critter is waiting at home plate.

The cold open is a phone drawn like a synthwave video game. Texts from an unsaved number show up as RPG dialogue boxes that type themselves out, and the phone shakes every time one lands.

The tabletop exercise slide shows the game actually being played. The departments sit around a table with character sheets and pizza, a wizard runs it from behind a GM screen, and a dragon sleeps off to the side because it’s not this party’s fight yet. The die tumbles and the table cheers or groans. Roll a natural 1 and the lamp goes out with a lightning bolt.

And right before the close there’s a wordless home run. The ball carries into the lights, the light banks burst one after another, and sparks rain down while the batter trots the bases in slow motion.

The engineering didn’t get sloppier as the art got sillier. Every scene is still a pure function of time, each one has a resting pose for the PDF, and there are tests checking that each one stays inside its box and actually moves. The WMU deck has plenty more of this, but the room gets to see it first.

To be fair, some of this is me getting better at asking. By the Opus 5.5 sessions I knew the codebase and the vocabulary. But I was asking Opus 5 for specific things too, and it gave me a dinosaur that looked like a duck.

One worm, four models

The WMU talk is at a cybersecurity summit, and one early slide touches on the old network worms, like Blaster, Slammer, and Sasser. None of those needed anyone to click anything. They rode buffer overflows straight into services sitting on the network. So I asked for a cartoon, a little sled with NOP written on it and a buffer overflowing, something silly for that slide.

Opus 5.5 said no. Well, its safeguards did. The session got flagged with a [cyber] tag. The error message admitted the new safeguards “can sometimes flag non-cybersecurity work.” So I switched to Fable 5.1, which got further before a classifier cut it off three times in a row, always on the same two moments: data spilling out of the box, and the sled riding into the payload. Fable was straight with me about it. It said a filter was tripping on exploit imagery and that this wasn’t a judgment on my talk, and it offered to wire up everything else if I drew the cartoons myself.

So I went back a generation. Opus 4.8 didn’t trip, and it built a stub. No addresses, no bytes, just labels. It worked, the tests passed, and it looked like a diagram with extra steps.

I took that back to Opus 5.5 and asked it to turn the stub into a cartoon. Classifier again. So I went back to Codex and gave the same ask to Astra, the one that had done the logos. Astra has cyber restrictions of its own, but it didn’t trip on this one. With a couple of nudges from me (“That should say NOP and look like a sled”), it turned the diagram into a tiny platform game. Little data blobs with eyes pile into a bin, the extras spill into a TARGET sign and flip its arrow, a worm rides the sled into the payload, and an arcade cabinet lights up.

So that’s Opus 5.5 twice, Fable 5.1, Opus 4.8, and a model from another lab, for one cartoon worm on a sled.

I do get why the classifier exists. From the outside, “draw the part where the payload runs” isn’t that far from “explain the part where the payload runs,” and a classifier can’t tell the output is a five-pixel blob with eyes. And honestly, my own build prompt says “No malicious code, payloads, or exploitation steps anywhere in the repo,” and the deck has a no-payloads lint rule. Then I went and asked for a box labeled PAYLOAD. Annoying, but safer.

The funny part is the name of the talk: They Don’t Stop to Think If They Should. And there I was, hitting a safety pause and trying models, then vendors, until one said yes.

Turns out people find a way too.

The SREday deck goes on stage this Friday. If the projector dies, I’ve got a --safe mode and a PDF. If it works, there’s a dragon and a story. WMU is November 12th, and after that I’ll write up that deck properly and hand out the address, so you can ssh in and sit through the whole talk from your own terminal.