<?xml version="1.0" encoding="utf-8"?>
<feed xml:lang="en-us" xmlns="http://www.w3.org/2005/Atom"><title>Simon Willison's Weblog: vibe-coding</title><link href="http://simonwillison.net/" rel="alternate"/><link href="http://simonwillison.net/tags/vibe-coding.atom" rel="self"/><id>http://simonwillison.net/</id><updated>2026-09-02T05:50:57+00:00</updated><author><name>Simon Willison</name></author><entry><title>Quoting Rick Brewster</title><link href="https://simonwillison.net/2026/Sep/2/rick-brewster/" rel="alternate"/><published>2026-09-02T05:50:57+00:00</published><updated>2026-09-02T05:50:57+00:00</updated><id>https://simonwillison.net/2026/Sep/2/rick-brewster/</id><summary type="html">
    &lt;blockquote cite="https://forums.paint.net/topic/134563-🍷-extremely-experimental-winelinux-support-how-to-get-started/"&gt;&lt;p&gt;Direct2D has always been the biggest hurdle for Paint.NET on WINE, and it's clear that it will never be completed enough for Paint.NET's use. And I can't just "disable" the use of Direct2D. So, instead, &lt;strong&gt;Paint.NET now has an internal, from-scratch, clean-room reverse-engineered rewrite of Direct2D that it uses on WINE&lt;/strong&gt; (triggered by using &lt;strong&gt;/wine&lt;/strong&gt;). It lives in &lt;strong&gt;PaintDotNet.Windows.Direct2D1.Managed.dll&lt;/strong&gt;. This was written by our good friend &lt;a href="https://claude.ai/"&gt;Claude&lt;/a&gt;, without whom this would NOT have been possible and would NEVER have happened. [...]&lt;/p&gt;
&lt;p&gt;Most of this code is, as they say, "vibe coded." By that I mean that it has not been thoroughly reviewed, it's more "trust me bro" style. I cannot possibly review 180,000 lines of code, it's just way way &lt;em&gt;way&lt;/em&gt; too much. For reference, the rest of Paint.NET is about 700,000 lines of code and I've been working on it for over 20 years. [...]&lt;/p&gt;
&lt;p&gt;At times, Claude was working with the fury of 10 freshly unshackled Einstein genius-level 10x coders. And other times ... well, not so much. I had to babysit Claude quite a bit to make sure it did resource management correctly (for awhile it just wasn't doing the COM equivalent of AddRef() for reference counted objects, oops). I had to slap it a few times when I found some really bad design or architecture decisions. And I was also impressed at some rather clever and tireless reverse engineering work it did to figure out all the formulas needed for implementing Direct2D's built-in effects library.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p class="cite"&gt;&amp;mdash; &lt;a href="https://forums.paint.net/topic/134563-🍷-extremely-experimental-winelinux-support-how-to-get-started/"&gt;Rick Brewster&lt;/a&gt;, author of Paint.NET&lt;/p&gt;

    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/reverse-engineering"&gt;reverse-engineering&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/coding-agents"&gt;coding-agents&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/claude"&gt;claude&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/generative-ai"&gt;generative-ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai"&gt;ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/llms"&gt;llms&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/dotnet"&gt;dotnet&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/linux"&gt;linux&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/vibe-coding"&gt;vibe-coding&lt;/a&gt;&lt;/p&gt;



</summary><category term="reverse-engineering"/><category term="coding-agents"/><category term="claude"/><category term="generative-ai"/><category term="ai"/><category term="llms"/><category term="dotnet"/><category term="linux"/><category term="vibe-coding"/></entry><entry><title>Stop Making TUIs</title><link href="https://simonwillison.net/2026/Aug/21/stop-making-tuis/" rel="alternate"/><published>2026-08-21T16:07:32+00:00</published><updated>2026-08-21T16:07:32+00:00</updated><id>https://simonwillison.net/2026/Aug/21/stop-making-tuis/</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://sockpuppet.org/blog/2026/08/20/stop-making-tuis/"&gt;Stop Making TUIs&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Thomas Ptacek advocates for building real native user interfaces for even the smallest of personal tools, because coding agents have reduced the cost of getting a usable-enough GUI up and running to almost nothing.&lt;/p&gt;
&lt;p&gt;I wrote about my vibe-coded bandwidth and GPU monitoring macOS task bar apps &lt;a href="https://simonwillison.net/2026/Mar/27/vibe-coding-swiftui/"&gt;back in March&lt;/a&gt;, and I'm still using both of those on a daily basis.&lt;/p&gt;
&lt;p&gt;I'm not habitually knocking out real UIs for my other projects yet, but I'm running out of excuses!&lt;/p&gt;
&lt;p&gt;Thomas:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you haven’t tried your hand at turning one of your 500 throwaway CLIs into a native app, you’re doing yourself a disservice. Go build a native UI. It’ll probably change the way you think.&lt;/p&gt;
&lt;/blockquote&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/thomas-ptacek"&gt;thomas-ptacek&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai"&gt;ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/generative-ai"&gt;generative-ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/llms"&gt;llms&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/vibe-coding"&gt;vibe-coding&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/coding-agents"&gt;coding-agents&lt;/a&gt;&lt;/p&gt;



</summary><category term="thomas-ptacek"/><category term="ai"/><category term="generative-ai"/><category term="llms"/><category term="vibe-coding"/><category term="coding-agents"/></entry><entry><title>One-shotting a Raccoon Heist game using Claude Fable 5</title><link href="https://simonwillison.net/2026/Aug/5/raccoon-heist/" rel="alternate"/><published>2026-08-05T19:42:38+00:00</published><updated>2026-08-05T19:42:38+00:00</updated><id>https://simonwillison.net/2026/Aug/5/raccoon-heist/</id><summary type="html">
    &lt;p&gt;Back in 2022 &lt;a href="https://twitter.com/simonw/status/1555626060384911360"&gt;I tweeted&lt;/a&gt; screenshots of a game concept generated by GPT-3 and some concept "art" created using DALL-E. Today, on the fourth anniversary of that tweet, I decided to see if Claude Fable 5 (running in &lt;a href="https://code.claude.com/docs/en/claude-code-on-the-web"&gt;Claude Code for web&lt;/a&gt;) could build the entire game from the content of that tweet. It did a pretty good job of it!&lt;/p&gt;
&lt;p&gt;You can &lt;a href="https://simonw.github.io/raccoon-heist/"&gt;play the game here&lt;/a&gt;. Here's &lt;a href="https://github.com/simonw/raccoon-heist/"&gt;the GitHub repo&lt;/a&gt;, and a short video demo:&lt;/p&gt;

&lt;p&gt;&lt;video
    controls="controls"
    preload="none"
    poster="https://static.simonwillison.net/static/2026/raccoon-heist-poster.jpg"
    width="1280"
    height="720"
    style="display: block; width: 100%; height: auto;"
  &gt;
    &lt;source src="https://static.simonwillison.net/static/2026/raccoon-heist-720p.mp4" type="video/mp4" /&gt;
    Your browser does not support HTML5 video.
  &lt;/video&gt;
&lt;/p&gt;

&lt;h4 id="how-i-built-this"&gt;How I built this&lt;/h4&gt;
&lt;p&gt;This is &lt;a href="https://twitter.com/simonw/status/1555626060384911360"&gt;the August 5th, 2022 tweet&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://static.simonwillison.net/static/2026/raccoon-heist-tweet.webp" alt="Simon Willison @simonw - New hobby: prototyping video games in 60 seconds using a combination of GPT-3 and DALL-E Here's &amp;quot;Raccoon Heist&amp;quot;. Two images - the first is a screenshot of the GPT-3 playground, prompt below. On the right is a very clearly DALL-E generated image of an almost-isometric 3D computer game, with two raccoons sneaking down a street past a futuristic looking building, looking like they want to steal something." style="max-width: 100%;" /&gt;&lt;/p&gt;
&lt;p&gt;My GPT-3 prompt back then was:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;Write a detailed product description of a computer game where a team of raccoons go on heists.&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This was in the text completion days, where you asked GPT-3 to complete the rest of the text. It completed:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;In "Raccoon Heist", you and your team of thieving raccoons are tasked with pulling off a series of daring heists. From robbing banks to stealing priceless art, no job is too big or too small for your furry crew. You'll need to use your wits and your skills to avoid the police and make a clean getaway with the loot. With exciting gameplay and a charming cast of characters, "Raccoon Heist" is the perfect game for anyone looking for a light-hearted caper.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;My prompt to DALL-E was short:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;Screenshot from a video game where a team of raccoons go on a heist&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Today's experiment: can I dump those screenshots into Fable 5 with a prompt telling it to write a game, then leave it to its own devices and get a working game at the end?&lt;/p&gt;
&lt;h4 id="setting-claude-code-for-web-up-to-use-github-pages"&gt;Setting Claude Code for web up to use GitHub Pages&lt;/h4&gt;
&lt;p&gt;A frustrating thing about Claude Code for web is that it can be hard to test what it's working on while it's still working.&lt;/p&gt;
&lt;p&gt;I've been using GitHub Pages to work around that limitation, and found it to work really well.&lt;/p&gt;
&lt;p&gt;Here's my process:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create a new repository for the project at &lt;a href="https://github.com/new"&gt;https://github.com/new&lt;/a&gt; - this can be public or private, the trick works equally well for both.&lt;/li&gt;
&lt;li&gt;Start a Claude Code for web session, in the Claude iPhone or Desktop apps or in the browser at &lt;a href="https://claude.ai/code"&gt;https://claude.ai/code&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Tell Claude what to work on, and encourage it to commit an &lt;code&gt;index.html&lt;/code&gt; page as quickly as possible. This will create a branch with a name like &lt;code&gt;claude/3d-raccoon-heist-game-50n293&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Navigate to the Settings -&amp;gt; Pages area for the repository (&lt;code&gt;github.com/simonw/raccoon-heist/settings/pages&lt;/code&gt; in my case), select "Deploy from a branch", pick the branch name, and hit Save.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;That's all it takes! Within about 30 seconds of each push the latest content will be visible at &lt;code&gt;yourname.github.io/your-repo/&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If you do this with a private repo, anyone who can guess the name of the repo will be able to view the published content. I don't worry much about this myself.&lt;/p&gt;
&lt;h4 id="the-fable-5-prompt"&gt;The Fable 5 prompt&lt;/h4&gt;
&lt;p&gt;Here's the prompt I gave Fable 5 (written in the notes app on my phone - this entire project was conducted on mobile). I accompanied it with the two images from the original tweet.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;Build this 3D game, for the browser.&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;This repo is configured to serve static files so make sure there is an index.html that loads everything else.&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Make sure it is mobile-friendly (touch controls, works well on small screens).&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;You have an OpenAI API key and access to their image generation model APIs, use that for textures to use with your 3D models. Docs here: https://developers.openai.com/api/docs/guides/image-generation - use gpt-image-2&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Work independently - do not ask me to make any further design decisions. Make sure the game is fun, a little surprising, has good raccoon heist vibes, and is visually pleasing.&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Commit and push as often as possible so I can preview your work - start with an index.html that presents a title screen, then build from there.&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Append to a notes.md file as you work, including your changes to that as part of every commit.&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I didn't make any technology choices. I assumed (correctly) that it would probably use &lt;a href="https://threejs.org/"&gt;Three.js&lt;/a&gt; based on previous experiments.&lt;/p&gt;
&lt;p&gt;Giving Claude access to an OpenAI key turns out to work really well for filling in gaps in its capabilities - in this case we needed some way to generate images to use as textures. Fable is very good at prompting image generators!&lt;/p&gt;
&lt;p&gt;I said "Work independently - do not ask me to make any further design decisions" because I wanted to see if it could produce a full, working game without any further input from me.&lt;/p&gt;
&lt;p&gt;I also said "Commit and push as often as possible so I can preview your work". When you use Claude Code in the Claude iPhone app you give it a GitHub repository and it works in a branch. Telling it to "push as often as possible" means commits start landing in that branch straight away.&lt;/p&gt;
&lt;p&gt;I like asking for &lt;code&gt;notes.md&lt;/code&gt; as a bit of added flavor - here's &lt;a href="https://github.com/simonw/raccoon-heist/blob/main/notes.md"&gt;that finished file&lt;/a&gt;, and the entry it made when it added the dog:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;New escalation: from night 3 the yards get a patrolling guard dog — a low-poly brown hound with a spiked red collar and a wagging tail. It wanders between random spots, and within 12 units it catches your scent and tracks you by smell (line of sight is irrelevant — it's all nose, shown by a 👃 over its head and barking). It gives up if you open a 17-unit gap. Getting caught messages are now source-specific: guard / headlights / hound. Verified wander → track → caught with an automated test.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id="reviewing-the-transcript"&gt;Reviewing the transcript&lt;/h4&gt;
&lt;p&gt;You can access &lt;a href="https://claude.ai/code/session_01NUBoCfnhGETcCDyEUPS8jp"&gt;the Claude Code shared session&lt;/a&gt;, and I also used my &lt;a href="https://github.com/simonw/claude-code-transcripts"&gt;claude-code-transcripts&lt;/a&gt; tool to export my own HTML version which you &lt;a href="https://simonw.github.io/raccoon-heist/transcript/page-001.html"&gt;can find here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Fable started with an index page, &lt;a href="https://simonw.github.io/raccoon-heist/transcript/page-001.html#msg-2026-08-05T14-55-13-304Z"&gt;vendored a copy&lt;/a&gt; of Three.js, then wrote its own &lt;a href="https://simonw.github.io/raccoon-heist/transcript/page-001.html#msg-2026-08-05T14-55-49-064Z"&gt;gen_textures.py script&lt;/a&gt; (&lt;a href="https://github.com/simonw/raccoon-heist/blob/main/gen_textures.py"&gt;copy here&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;It generated the textures and &lt;a href="https://simonw.github.io/raccoon-heist/transcript/page-001.html#msg-2026-08-05T14-59-07-900Z"&gt;spot-checked them&lt;/a&gt; to make sure they looked OK. The &lt;a href="https://github.com/simonw/raccoon-heist/blob/main/textures/metal.jpg"&gt;metal.jpg file&lt;/a&gt; it generated for the trash can looks like this, though I don't think it was applied exactly right in the game itself:

&lt;p&gt;&lt;img src="https://raw.githubusercontent.com/simonw/raccoon-heist/refs/heads/main/textures/metal.jpg" alt="A game texture atlas of dark blue-grey riveted metal panels, showing a circular hatch with a handle in the top left, ribbed corrugated panels across the middle, a plain circular plate bottom left, and flat banded strips at top and bottom. No text visible." style="max-width: 100%" /&gt;&lt;/p&gt;

Then it built out the first basic version of the game, then &lt;a href="https://simonw.github.io/raccoon-heist/transcript/page-001.html#msg-2026-08-05T15-04-51-625Z"&gt;decided to&lt;/a&gt; "smoke-test in the pre-installed Chromium" using Playwright. This meant it could take screenshots of its own work and &lt;a href="https://simonw.github.io/raccoon-heist/transcript/page-001.html#msg-2026-08-05T15-05-53-823Z"&gt;eyeball them&lt;/a&gt;. It did that for both desktop and mobile widths of the page, then noticed that &lt;a href="https://simonw.github.io/raccoon-heist/transcript/page-001.html#msg-2026-08-05T15-09-33-406Z"&gt;the raccoon was invisible&lt;/a&gt; at mobile widths, so it &lt;a href="https://simonw.github.io/raccoon-heist/transcript/page-001.html#msg-2026-08-05T15-14-39-180Z"&gt;fixed that&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The raccoon, dumpster hideout, and both crew raccoons are now perfectly visible on mobile. Committing this critical fix.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It decided to generate a title screen, which &lt;a href="https://simonw.github.io/raccoon-heist/transcript/page-001.html#msg-2026-08-05T15-15-02-574Z"&gt;it did&lt;/a&gt; using this &lt;a href="https://github.com/simonw/raccoon-heist/blob/main/gen_title.py"&gt;gen_title.py&lt;/a&gt; script. Here's the &lt;code&gt;gpt-image-2&lt;/code&gt; prompt it used for that:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;Video game key art, low-poly 3D render style, moody nighttime scene: a cute low-poly raccoon wearing a tiny black burglar mask sneaking on its hind legs carrying a glowing gold coin, next to a tipped-over metal trash can, suburban house with warm glowing windows in the background, deep blue night, full moon, fireflies, cinematic rim lighting, charming heist caper mood. No text, no words, no logos.&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;And the resulting image (which Claude &lt;a href="https://simonw.github.io/raccoon-heist/transcript/page-001.html#msg-2026-08-05T15-16-42-176Z"&gt;thought was "gorgeous"&lt;/a&gt;) - though I note that when it's shown on desktop it gets cropped to just the top third without the raccoon!&lt;/p&gt;
&lt;p&gt;&lt;img src="https://static.simonwillison.net/static/2026/raccoon-heist-title.jpeg" alt="Polygon raccoon holding a gold coin next to an overturned trash can, a house and the moon in the background." style="max-width: 100%;" /&gt;&lt;/p&gt;
&lt;p&gt;Then my favorite change: it &lt;a href="https://simonw.github.io/raccoon-heist/transcript/page-001.html#msg-2026-08-05T15-23-00-850Z"&gt;added the dog&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight highlight-source-js"&gt;&lt;pre&gt;&lt;span class="pl-k"&gt;export&lt;/span&gt; &lt;span class="pl-k"&gt;function&lt;/span&gt; &lt;span class="pl-en"&gt;makeDog&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt; &lt;span class="pl-kos"&gt;{&lt;/span&gt;
  &lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-s1"&gt;g&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-k"&gt;new&lt;/span&gt; &lt;span class="pl-c1"&gt;THREE&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;Group&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
  &lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-c1"&gt;BROWN&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-c1"&gt;0x8a6440&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-c1"&gt;DARK&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-c1"&gt;0x5e4128&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
  &lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-s1"&gt;body&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-k"&gt;new&lt;/span&gt; &lt;span class="pl-c1"&gt;THREE&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;Mesh&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-k"&gt;new&lt;/span&gt; &lt;span class="pl-c1"&gt;THREE&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;SphereGeometry&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-c1"&gt;0.42&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-c1"&gt;10&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-c1"&gt;8&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-v"&gt;M&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-c1"&gt;BROWN&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
  &lt;span class="pl-s1"&gt;body&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;scale&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;set&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-c1"&gt;0.9&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-c1"&gt;0.8&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-c1"&gt;1.5&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
  &lt;span class="pl-s1"&gt;body&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;position&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;y&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-c1"&gt;0.55&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
  &lt;span class="pl-s1"&gt;body&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;castShadow&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-c1"&gt;true&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
  &lt;span class="pl-s1"&gt;g&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;add&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s1"&gt;body&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
  &lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-s1"&gt;head&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-k"&gt;new&lt;/span&gt; &lt;span class="pl-c1"&gt;THREE&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;Mesh&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-k"&gt;new&lt;/span&gt; &lt;span class="pl-c1"&gt;THREE&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;SphereGeometry&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-c1"&gt;0.3&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-c1"&gt;10&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-c1"&gt;8&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-v"&gt;M&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-c1"&gt;BROWN&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
  &lt;span class="pl-s1"&gt;head&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;position&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;set&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-c1"&gt;0&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-c1"&gt;0.85&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-c1"&gt;0.62&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
  &lt;span class="pl-s1"&gt;g&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;add&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s1"&gt;head&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
  &lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-s1"&gt;snout&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-k"&gt;new&lt;/span&gt; &lt;span class="pl-c1"&gt;THREE&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;Mesh&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-k"&gt;new&lt;/span&gt; &lt;span class="pl-c1"&gt;THREE&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;SphereGeometry&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-c1"&gt;0.16&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-c1"&gt;8&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-c1"&gt;6&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-v"&gt;M&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-c1"&gt;DARK&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
  &lt;span class="pl-s1"&gt;snout&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;scale&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;set&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-c1"&gt;0.9&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-c1"&gt;0.7&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-c1"&gt;1.3&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
  &lt;span class="pl-s1"&gt;snout&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;position&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;set&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-c1"&gt;0&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-c1"&gt;0.76&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-c1"&gt;0.9&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
  &lt;span class="pl-s1"&gt;g&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;add&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s1"&gt;snout&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
  &lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-s1"&gt;nose&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-k"&gt;new&lt;/span&gt; &lt;span class="pl-c1"&gt;THREE&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;Mesh&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-k"&gt;new&lt;/span&gt; &lt;span class="pl-c1"&gt;THREE&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;SphereGeometry&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-c1"&gt;0.06&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-c1"&gt;6&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-c1"&gt;6&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-v"&gt;M&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-c1"&gt;BLACK&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
  &lt;span class="pl-s1"&gt;nose&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;position&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;set&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-c1"&gt;0&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-c1"&gt;0.78&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-c1"&gt;1.08&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
  &lt;span class="pl-s1"&gt;g&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;add&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s1"&gt;nose&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
  &lt;span class="pl-k"&gt;for&lt;/span&gt; &lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-s1"&gt;s&lt;/span&gt; &lt;span class="pl-k"&gt;of&lt;/span&gt; &lt;span class="pl-kos"&gt;[&lt;/span&gt;&lt;span class="pl-c1"&gt;-&lt;/span&gt;&lt;span class="pl-c1"&gt;1&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-c1"&gt;1&lt;/span&gt;&lt;span class="pl-kos"&gt;]&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt; &lt;span class="pl-kos"&gt;{&lt;/span&gt;
    &lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-s1"&gt;ear&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-k"&gt;new&lt;/span&gt; &lt;span class="pl-c1"&gt;THREE&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;Mesh&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-k"&gt;new&lt;/span&gt; &lt;span class="pl-c1"&gt;THREE&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;SphereGeometry&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-c1"&gt;0.12&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-c1"&gt;6&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-c1"&gt;6&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-v"&gt;M&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-c1"&gt;DARK&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
    &lt;span class="pl-s1"&gt;ear&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;scale&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;set&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-c1"&gt;0.7&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-c1"&gt;1.3&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-c1"&gt;0.5&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
    &lt;span class="pl-s1"&gt;ear&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;position&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;set&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-c1"&gt;0.2&lt;/span&gt; &lt;span class="pl-c1"&gt;*&lt;/span&gt; &lt;span class="pl-s1"&gt;s&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-c1"&gt;1.08&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-c1"&gt;0.55&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
    &lt;span class="pl-s1"&gt;g&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;add&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s1"&gt;ear&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
    &lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-s1"&gt;eye&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-k"&gt;new&lt;/span&gt; &lt;span class="pl-c1"&gt;THREE&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;Mesh&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-k"&gt;new&lt;/span&gt; &lt;span class="pl-c1"&gt;THREE&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;SphereGeometry&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-c1"&gt;0.05&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-c1"&gt;6&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-c1"&gt;6&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-v"&gt;M&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-c1"&gt;0x1a1a1a&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-kos"&gt;{&lt;/span&gt; &lt;span class="pl-c1"&gt;emissive&lt;/span&gt;: &lt;span class="pl-c1"&gt;0x331111&lt;/span&gt; &lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
    &lt;span class="pl-s1"&gt;eye&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;position&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;set&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-c1"&gt;0.13&lt;/span&gt; &lt;span class="pl-c1"&gt;*&lt;/span&gt; &lt;span class="pl-s1"&gt;s&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-c1"&gt;0.92&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-c1"&gt;0.86&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
    &lt;span class="pl-s1"&gt;g&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;add&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s1"&gt;eye&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
  &lt;span class="pl-kos"&gt;}&lt;/span&gt;
  &lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-s1"&gt;tail&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-k"&gt;new&lt;/span&gt; &lt;span class="pl-c1"&gt;THREE&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;Mesh&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-k"&gt;new&lt;/span&gt; &lt;span class="pl-c1"&gt;THREE&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;CylinderGeometry&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-c1"&gt;0.05&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-c1"&gt;0.09&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-c1"&gt;0.5&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-c1"&gt;6&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-v"&gt;M&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-c1"&gt;DARK&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
  &lt;span class="pl-s1"&gt;tail&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;position&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;set&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-c1"&gt;0&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-c1"&gt;0.8&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-c1"&gt;-&lt;/span&gt;&lt;span class="pl-c1"&gt;0.62&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
  &lt;span class="pl-s1"&gt;tail&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;rotation&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;x&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-c1"&gt;0.8&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
  &lt;span class="pl-s1"&gt;g&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;add&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s1"&gt;tail&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
  &lt;span class="pl-c"&gt;// spiked collar&lt;/span&gt;
  &lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-s1"&gt;collar&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-k"&gt;new&lt;/span&gt; &lt;span class="pl-c1"&gt;THREE&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;Mesh&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-k"&gt;new&lt;/span&gt; &lt;span class="pl-c1"&gt;THREE&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;TorusGeometry&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-c1"&gt;0.22&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-c1"&gt;0.05&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-c1"&gt;6&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-c1"&gt;12&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-v"&gt;M&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-c1"&gt;0xc0392b&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
  &lt;span class="pl-s1"&gt;collar&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;position&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;set&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-c1"&gt;0&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-c1"&gt;0.78&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-c1"&gt;0.5&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
  &lt;span class="pl-s1"&gt;collar&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;rotation&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;x&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-v"&gt;Math&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;PI&lt;/span&gt; &lt;span class="pl-c1"&gt;/&lt;/span&gt; &lt;span class="pl-c1"&gt;2.4&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
  &lt;span class="pl-s1"&gt;g&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;add&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s1"&gt;collar&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
  &lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-s1"&gt;legGeo&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-k"&gt;new&lt;/span&gt; &lt;span class="pl-c1"&gt;THREE&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;CylinderGeometry&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-c1"&gt;0.07&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-c1"&gt;0.09&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-c1"&gt;0.34&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-c1"&gt;6&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
  &lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-s1"&gt;legs&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-kos"&gt;[&lt;/span&gt;&lt;span class="pl-kos"&gt;]&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
  &lt;span class="pl-k"&gt;for&lt;/span&gt; &lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-kos"&gt;[&lt;/span&gt;&lt;span class="pl-s1"&gt;x&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-s1"&gt;z&lt;/span&gt;&lt;span class="pl-kos"&gt;]&lt;/span&gt; &lt;span class="pl-k"&gt;of&lt;/span&gt; &lt;span class="pl-kos"&gt;[&lt;/span&gt;&lt;span class="pl-kos"&gt;[&lt;/span&gt;&lt;span class="pl-c1"&gt;-&lt;/span&gt;&lt;span class="pl-c1"&gt;0.22&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-c1"&gt;0.35&lt;/span&gt;&lt;span class="pl-kos"&gt;]&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-kos"&gt;[&lt;/span&gt;&lt;span class="pl-c1"&gt;0.22&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-c1"&gt;0.35&lt;/span&gt;&lt;span class="pl-kos"&gt;]&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-kos"&gt;[&lt;/span&gt;&lt;span class="pl-c1"&gt;-&lt;/span&gt;&lt;span class="pl-c1"&gt;0.22&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-c1"&gt;-&lt;/span&gt;&lt;span class="pl-c1"&gt;0.35&lt;/span&gt;&lt;span class="pl-kos"&gt;]&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-kos"&gt;[&lt;/span&gt;&lt;span class="pl-c1"&gt;0.22&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-c1"&gt;-&lt;/span&gt;&lt;span class="pl-c1"&gt;0.35&lt;/span&gt;&lt;span class="pl-kos"&gt;]&lt;/span&gt;&lt;span class="pl-kos"&gt;]&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt; &lt;span class="pl-kos"&gt;{&lt;/span&gt;
    &lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-s1"&gt;leg&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-k"&gt;new&lt;/span&gt; &lt;span class="pl-c1"&gt;THREE&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;Mesh&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s1"&gt;legGeo&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-v"&gt;M&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-c1"&gt;DARK&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
    &lt;span class="pl-s1"&gt;leg&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;position&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;set&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s1"&gt;x&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-c1"&gt;0.17&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-s1"&gt;z&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
    &lt;span class="pl-s1"&gt;g&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;add&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s1"&gt;leg&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
    &lt;span class="pl-s1"&gt;legs&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;push&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s1"&gt;leg&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
  &lt;span class="pl-kos"&gt;}&lt;/span&gt;
  &lt;span class="pl-k"&gt;let&lt;/span&gt; &lt;span class="pl-s1"&gt;phase&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-v"&gt;Math&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;random&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt; &lt;span class="pl-c1"&gt;*&lt;/span&gt; &lt;span class="pl-c1"&gt;10&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
  &lt;span class="pl-k"&gt;return&lt;/span&gt; &lt;span class="pl-kos"&gt;{&lt;/span&gt;
    &lt;span class="pl-c1"&gt;group&lt;/span&gt;: &lt;span class="pl-s1"&gt;g&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt;
    &lt;span class="pl-en"&gt;animate&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s1"&gt;dt&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-s1"&gt;speed&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt; &lt;span class="pl-kos"&gt;{&lt;/span&gt;
      &lt;span class="pl-s1"&gt;phase&lt;/span&gt; &lt;span class="pl-c1"&gt;+=&lt;/span&gt; &lt;span class="pl-s1"&gt;dt&lt;/span&gt; &lt;span class="pl-c1"&gt;*&lt;/span&gt; &lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-c1"&gt;3&lt;/span&gt; &lt;span class="pl-c1"&gt;+&lt;/span&gt; &lt;span class="pl-s1"&gt;speed&lt;/span&gt; &lt;span class="pl-c1"&gt;*&lt;/span&gt; &lt;span class="pl-c1"&gt;10&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
      &lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-s1"&gt;amp&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-v"&gt;Math&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;min&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-c1"&gt;0.6&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-c1"&gt;0.1&lt;/span&gt; &lt;span class="pl-c1"&gt;+&lt;/span&gt; &lt;span class="pl-s1"&gt;speed&lt;/span&gt; &lt;span class="pl-c1"&gt;*&lt;/span&gt; &lt;span class="pl-c1"&gt;0.6&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
      &lt;span class="pl-s1"&gt;legs&lt;/span&gt;&lt;span class="pl-kos"&gt;[&lt;/span&gt;&lt;span class="pl-c1"&gt;0&lt;/span&gt;&lt;span class="pl-kos"&gt;]&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;rotation&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;x&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-v"&gt;Math&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;sin&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s1"&gt;phase&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt; &lt;span class="pl-c1"&gt;*&lt;/span&gt; &lt;span class="pl-s1"&gt;amp&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
      &lt;span class="pl-s1"&gt;legs&lt;/span&gt;&lt;span class="pl-kos"&gt;[&lt;/span&gt;&lt;span class="pl-c1"&gt;3&lt;/span&gt;&lt;span class="pl-kos"&gt;]&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;rotation&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;x&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-v"&gt;Math&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;sin&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s1"&gt;phase&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt; &lt;span class="pl-c1"&gt;*&lt;/span&gt; &lt;span class="pl-s1"&gt;amp&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
      &lt;span class="pl-s1"&gt;legs&lt;/span&gt;&lt;span class="pl-kos"&gt;[&lt;/span&gt;&lt;span class="pl-c1"&gt;1&lt;/span&gt;&lt;span class="pl-kos"&gt;]&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;rotation&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;x&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-c1"&gt;-&lt;/span&gt;&lt;span class="pl-v"&gt;Math&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;sin&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s1"&gt;phase&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt; &lt;span class="pl-c1"&gt;*&lt;/span&gt; &lt;span class="pl-s1"&gt;amp&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
      &lt;span class="pl-s1"&gt;legs&lt;/span&gt;&lt;span class="pl-kos"&gt;[&lt;/span&gt;&lt;span class="pl-c1"&gt;2&lt;/span&gt;&lt;span class="pl-kos"&gt;]&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;rotation&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;x&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-c1"&gt;-&lt;/span&gt;&lt;span class="pl-v"&gt;Math&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;sin&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s1"&gt;phase&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt; &lt;span class="pl-c1"&gt;*&lt;/span&gt; &lt;span class="pl-s1"&gt;amp&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
      &lt;span class="pl-s1"&gt;tail&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;rotation&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;z&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-v"&gt;Math&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;sin&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s1"&gt;phase&lt;/span&gt; &lt;span class="pl-c1"&gt;*&lt;/span&gt; &lt;span class="pl-c1"&gt;1.5&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt; &lt;span class="pl-c1"&gt;*&lt;/span&gt; &lt;span class="pl-c1"&gt;0.4&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
      &lt;span class="pl-s1"&gt;body&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;position&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;y&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-c1"&gt;0.55&lt;/span&gt; &lt;span class="pl-c1"&gt;+&lt;/span&gt; &lt;span class="pl-v"&gt;Math&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;abs&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-v"&gt;Math&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;sin&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s1"&gt;phase&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt; &lt;span class="pl-c1"&gt;*&lt;/span&gt; &lt;span class="pl-c1"&gt;0.04&lt;/span&gt; &lt;span class="pl-c1"&gt;*&lt;/span&gt; &lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-c1"&gt;0.3&lt;/span&gt; &lt;span class="pl-c1"&gt;+&lt;/span&gt; &lt;span class="pl-s1"&gt;speed&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
    &lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt;
  &lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
&lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And did a &lt;a href="https://simonw.github.io/raccoon-heist/transcript/page-001.html#msg-2026-08-05T15-24-09-230Z"&gt;round of testing on it&lt;/a&gt; using Playwright, including &lt;a href="https://simonw.github.io/raccoon-heist/transcript/page-001.html#msg-2026-08-05T15-24-33-559Z"&gt;another screenshot&lt;/a&gt;.&lt;/p&gt;
&lt;div class="highlight highlight-source-js"&gt;&lt;pre&gt;  &lt;span class="pl-c"&gt;// walk near the dog&lt;/span&gt;
  &lt;span class="pl-k"&gt;await&lt;/span&gt; &lt;span class="pl-s1"&gt;page&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;evaluate&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt; &lt;span class="pl-c1"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="pl-kos"&gt;{&lt;/span&gt; &lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-s1"&gt;d&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-smi"&gt;window&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;__rh&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;dog&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt; &lt;span class="pl-smi"&gt;window&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;__rh&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;teleport&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s1"&gt;d&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;x&lt;/span&gt; &lt;span class="pl-c1"&gt;+&lt;/span&gt; &lt;span class="pl-c1"&gt;6&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-s1"&gt;d&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;z&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt; &lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
  &lt;span class="pl-k"&gt;await&lt;/span&gt; &lt;span class="pl-s1"&gt;page&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;waitForTimeout&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-c1"&gt;2000&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
  &lt;span class="pl-s1"&gt;info&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-k"&gt;await&lt;/span&gt; &lt;span class="pl-s1"&gt;page&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;evaluate&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt; &lt;span class="pl-c1"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="pl-c1"&gt;JSON&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;stringify&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;{&lt;/span&gt; &lt;span class="pl-c1"&gt;dog&lt;/span&gt;: &lt;span class="pl-smi"&gt;window&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;__rh&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;dog&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-c1"&gt;state&lt;/span&gt;: &lt;span class="pl-smi"&gt;window&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;__rh&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;state&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-c1"&gt;player&lt;/span&gt;: &lt;span class="pl-smi"&gt;window&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;__rh&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;debug&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;player&lt;/span&gt; &lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
  &lt;span class="pl-smi"&gt;console&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;log&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s"&gt;'after approach:'&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-s1"&gt;info&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
  &lt;span class="pl-k"&gt;await&lt;/span&gt; &lt;span class="pl-s1"&gt;page&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;waitForTimeout&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-c1"&gt;3000&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
  &lt;span class="pl-s1"&gt;info&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-k"&gt;await&lt;/span&gt; &lt;span class="pl-s1"&gt;page&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;evaluate&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt; &lt;span class="pl-c1"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="pl-c1"&gt;JSON&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;stringify&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;{&lt;/span&gt; &lt;span class="pl-c1"&gt;dog&lt;/span&gt;: &lt;span class="pl-smi"&gt;window&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;__rh&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;dog&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-c1"&gt;state&lt;/span&gt;: &lt;span class="pl-smi"&gt;window&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;__rh&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;state&lt;/span&gt; &lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
  &lt;span class="pl-smi"&gt;console&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;log&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s"&gt;'after chase:'&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-s1"&gt;info&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
  &lt;span class="pl-k"&gt;await&lt;/span&gt; &lt;span class="pl-s1"&gt;page&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;screenshot&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;{&lt;/span&gt; &lt;span class="pl-c1"&gt;path&lt;/span&gt;: &lt;span class="pl-s1"&gt;__dirname&lt;/span&gt; &lt;span class="pl-c1"&gt;+&lt;/span&gt; &lt;span class="pl-s"&gt;'/shot-dog.png'&lt;/span&gt; &lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src="https://static.simonwillison.net/static/2026/shot-dog.png" alt="Screenshot of the game. Night 3, the raccoon is in the middle intersecting with the dog. A message reads Busted! The Guard shooed you off! The screen has a red tinge around the edges." style="max-width: 100%;" /&gt;&lt;/p&gt;
&lt;p&gt;It made a few more tweaks, then &lt;a href="https://simonw.github.io/raccoon-heist/transcript/page-001.html#msg-2026-08-05T15-29-13-245Z"&gt;declared itself done&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Raccoon Heist is built, tested, and pushed&lt;/strong&gt; — 7 commits on &lt;code&gt;claude/3d-raccoon-heist-game-50n293&lt;/code&gt;, all verified with automated Playwright playthroughs on desktop, portrait-phone, and landscape-phone viewports.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The game:&lt;/strong&gt; You're a masked raccoon sneaking out of your crew's dumpster hideout into a moonlit cul-de-sac. Grab coins, gems, rings, donuts, and fish (they visibly stack on your back and slow you down), then waddle home and bank them before dawn. Each night raises the goal and the heat: night 1 has one flashlight-sweeping guard, night 2 has two, night 3 adds a scent-tracking hound that ignores line-of-sight entirely. A police cruiser sweeps the street with headlights, homeowners randomly peek out of windows, a seagull periodically dive-bombs your unclaimed loot, pizza slices trigger a 6-second speed FRENZY, and a rare golden TV is worth 120 points if you can haul its weight home. Win ranks: TRASH PANDA ⭐ → CAT BURGLAR ⭐⭐ → MASTER OF DISGUISE ⭐⭐⭐, with your best run saved to localStorage.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How it's built:&lt;/strong&gt; Vendored Three.js (no CDN), procedural low-poly characters, and a procedural WebAudio soundtrack (sneaky walking-bass jazz plus all sound effects — zero audio files). All seven ground/wall textures and the title-screen hero art were generated with gpt-image-2 and committed as static assets, so the deployed game makes no API calls. Mobile gets a dynamic touch joystick (appears wherever you touch) plus a DASH button; desktop uses WASD/arrows and Space.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Testing caught two real bugs worth mentioning:&lt;/strong&gt; the canvas rendered at 2× size on any real phone (a &lt;code&gt;cssText&lt;/code&gt; assignment was wiping Three.js's inline sizing — desktop DPR-1 tests masked it completely), and the win screen's star-rating div inherited the title screen's full-screen &lt;code&gt;.stars&lt;/code&gt; CSS and silently swallowed every tap on the "next night" button. Both are fixed and regression-tested, along with pickup, banking, chase/caught, dawn, night progression, and lose/retry flows.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;notes.md&lt;/code&gt; has the full build log. The game is live on the branch — just serve the repo root and open &lt;code&gt;index.html&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id="is-the-game-any-good-"&gt;Is the game any good?&lt;/h4&gt;
&lt;p&gt;It's an impressive starting point, but it's not a &lt;em&gt;good&lt;/em&gt; game.&lt;/p&gt;
&lt;p&gt;You move your raccoon around a yard collecting items - donuts, fish, gold coins, jewels - while avoiding guards with flashlights and, in later levels, a dog. You have a limited carrying capacity, and once that's full you need to drop stuff off at the dumpster. If you pick up a pizza slice you get a temporary speed boost.&lt;/p&gt;
&lt;p&gt;There are no team mechanics at all - there are two other static raccoons next to the dumpster but they're purely decoration.&lt;/p&gt;
&lt;p&gt;It gets slightly more challenging as the levels progress - the dog introduced in level 3 is the most interesting new mechanic - but it's very, very easy to beat. It's also pretty boring - each night has a fixed duration and you can collect all of the items and then have nothing else to do while waiting for the dawn.&lt;/p&gt;
&lt;p&gt;I was impressed by the implementation. It's fully 3D, there are trash cans, the flashlight illumination cones are fun, and it has a reasonably coherent visual style. It works on mobile. The music ("a procedural WebAudio soundtrack (sneaky walking-bass jazz plus all sound effects — zero audio files)" according to Claude) is simple but feels about right.&lt;/p&gt;
&lt;p&gt;As a finished game project, it's mediocre. As a starting point from a single prompt I think it's very impressive.&lt;/p&gt;
&lt;p&gt;I've vibe coded up quite a few games now. They've all been deeply disappointing from a gameplay perspective - it turns out designing games that are &lt;em&gt;fun&lt;/em&gt; remains a uniquely human trait, and one which requires significantly more skill and experience than either Claude or I can bring to bear.&lt;/p&gt;
&lt;p&gt;That said, I thoroughly recommend tinkering with game development projects as a way to explore the capabilities of agents. It's a fun, low-risk way to try out new things. If you stick at it long enough you might even produce something that's worth playing!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Update 7th August 2026&lt;/strong&gt;: I posed the same prompt to OpenAI Codex Desktop running GPT-5.6 Sol Ultra and got a &lt;a href="https://simonwillison.net/2026/Aug/7/moonlight-mayhem/"&gt;significantly better result&lt;/a&gt; - GPT-5.6 Sol picked up on the importance of the squad of raccoons going on a heist, and built a game where you must rescue your two crewmates in a museum and then stack on top of them to steal the Golden Sardine.&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/game-design"&gt;game-design&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai"&gt;ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/prompt-engineering"&gt;prompt-engineering&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/generative-ai"&gt;generative-ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/llms"&gt;llms&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/anthropic"&gt;anthropic&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/claude"&gt;claude&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/text-to-image"&gt;text-to-image&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/vibe-coding"&gt;vibe-coding&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/coding-agents"&gt;coding-agents&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/claude-mythos-fable"&gt;claude-mythos-fable&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="game-design"/><category term="ai"/><category term="prompt-engineering"/><category term="generative-ai"/><category term="llms"/><category term="anthropic"/><category term="claude"/><category term="text-to-image"/><category term="vibe-coding"/><category term="coding-agents"/><category term="claude-mythos-fable"/></entry><entry><title>Porting the Moebius 0.2B image inpainting model to run in the browser with Claude Code</title><link href="https://simonwillison.net/2026/Jun/22/porting-moebius/" rel="alternate"/><published>2026-06-22T23:43:51+00:00</published><updated>2026-06-22T23:43:51+00:00</updated><id>https://simonwillison.net/2026/Jun/22/porting-moebius/</id><summary type="html">
    &lt;p&gt;This morning &lt;a href="https://news.ycombinator.com/item?id=48630171"&gt;on Hacker News&lt;/a&gt; I saw &lt;a href="https://hustvl.github.io/Moebius/"&gt;Moebius: 0.2B Lightweight Image Inpainting Framework with 10B-Level Performance&lt;/a&gt;, describing a small but effective inpainting model - a model where you can mark regions of an image to remove and the model imagines what should fill the space. The released model &lt;a href="https://github.com/hustvl/Moebius/blob/9310b76e368f5f7a8ecdf06493231af279c9973b/requirements.txt#L1"&gt;required PyTorch and NVIDIA CUDA&lt;/a&gt;, but since it described itself as 0.2B I decided to try and get it running using WebGPU in a browser. TL;DR: I got it working, and you can try the demo at &lt;a href="https://simonw.github.io/moebius-web/"&gt;simonw.github.io/moebius-web/&lt;/a&gt;. Read on for the details.&lt;/p&gt;
&lt;h4 id="the-finished-tool"&gt;The finished tool&lt;/h4&gt;
&lt;p&gt;Here's a video demo of the finished tool:&lt;/p&gt;

&lt;video
width="1280"
height="1070"
poster="https://static.simonwillison.net/static/2026/inpainting_1280_poster.jpg"
preload="none"
controls="controls"
playsinline="playsinline"
style="max-width:100%;height:auto"&gt;
&lt;source src="https://static.simonwillison.net/static/2026/inpainting_1280.mp4" type="video/mp4" /&gt;
&lt;/video&gt;

&lt;p&gt;You can open any image in it (non-square images get letterboxed), highlight areas to remove, click the "Run inpaint" button and wait for the model to do its magic.&lt;/p&gt;
&lt;h4 id="a-parallel-agent-side-project"&gt;A parallel agent side-project&lt;/h4&gt;
&lt;p&gt;My main project for today was landing a major feature in Datasette: a UI for creating and altering tables, as a follow-up to the &lt;a href="https://simonwillison.net/2026/Jun/16/datasette/"&gt;insert and edit rows feature&lt;/a&gt; I released last week.&lt;/p&gt;
&lt;p&gt;I was working on that in Codex Desktop (here's &lt;a href="https://github.com/simonw/datasette/pull/2789"&gt;the PR&lt;/a&gt;) and often found myself spending 5-10 minutes spinning my fingers waiting for it to complete a mid-sized refactor or add the finishing touches to a change to the UI.&lt;/p&gt;
&lt;p&gt;(An amusing thing about coding agents is that the harder a problem is the &lt;em&gt;more&lt;/em&gt; time you have to get distracted while you wait for them to finish crunching!)&lt;/p&gt;
&lt;p&gt;So I decided to spin up Claude Code in a terminal window and see how far I could get at porting Moebius to the web.&lt;/p&gt;
&lt;h4 id="some-agentic-research-to-kick-off-the-project"&gt;Some agentic research to kick off the project&lt;/h4&gt;
&lt;p&gt;My first step was to ask regular Claude about the feasibility of this project. In &lt;a href="https://claude.ai/"&gt;Claude.ai&lt;/a&gt;, which has the ability to clone repos from GitHub:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;Clone https://github.com/hustvl/Moebius/ and tell me if they published the code and weights to run this model anywhere&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;(I hadn't spotted the link to the weights yet, that's tucked away in the "News" section.)&lt;/p&gt;
&lt;p&gt;Then:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;For Moebius what are the options for running it right now - Python and NVIDIA CUDA only or other options too?&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;And:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;Muse on the feasibility of porting it to Transformers.js or similar and running it in a browser&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I like telling models to "muse on X", it's the shortest way I've found of expressing that I want them to contemplate a problem for me without providing them with a concrete goal.&lt;/p&gt;
&lt;p&gt;Here's &lt;a href="https://claude.ai/share/551c3dc8-17ce-4a4b-a0c9-8cbded6c7bf1"&gt;that chat transcript&lt;/a&gt;. I copied out the last answer and saved it as &lt;a href="https://github.com/simonw/moebius-web/blob/main/research.md"&gt;research.md&lt;/a&gt; for Claude Code to read later.&lt;/p&gt;
&lt;p&gt;Claude suggested using &lt;strong&gt;ONNX Runtime Web on the WebGPU backend&lt;/strong&gt; - the layer &lt;em&gt;below&lt;/em&gt; the &lt;a href="https://huggingface.co/docs/transformers.js/en/index"&gt;Transformers.js&lt;/a&gt; library I had suggested.&lt;/p&gt;
&lt;p&gt;That was enough to convince me it was worth setting Claude Code loose and seeing how far it could get.&lt;/p&gt;
&lt;p&gt;I usually start projects like this by gathering as much information as the coding agent might need as possible. Since I didn't expect this project to actually work I did everything in my &lt;code&gt;/tmp&lt;/code&gt; folder:&lt;/p&gt;
&lt;div class="highlight highlight-source-shell"&gt;&lt;pre&gt;&lt;span class="pl-c1"&gt;cd&lt;/span&gt; /tmp
mkdir Moebius
&lt;span class="pl-c1"&gt;cd&lt;/span&gt; Moebius
&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; Grab the Moebius python code&lt;/span&gt;
git clone https://github.com/hustvl/Moebius
&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; And the model weights (Claude figured this out):&lt;/span&gt;
GIT_LFS_SKIP_SMUDGE=0 git clone \
  https://huggingface.co/hustvl/Moebius Moebius-weights
&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; Finally a couple of libraries we might use:&lt;/span&gt;
git clone https://github.com/huggingface/transformers.js
git clone https://github.com/microsoft/onnxruntime&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id="setting-off-claude-code"&gt;Setting off Claude Code&lt;/h4&gt;
&lt;p&gt;I created a directory for the rest of the project and ran &lt;code&gt;git init&lt;/code&gt; in that so Claude could start committing code notes:&lt;/p&gt;
&lt;div class="highlight highlight-source-shell"&gt;&lt;pre&gt;mkdir /tmp/Moebius/moebius-web
&lt;span class="pl-c1"&gt;cd&lt;/span&gt; /tmp/Moebius/moebius-web
git init
&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; Copy in that research.md from earlier&lt;/span&gt;
git add research.md
git commit -m &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;Initial research by Claude Opus 4.8&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I fired up a &lt;code&gt;claude&lt;/code&gt; instance in the &lt;code&gt;/tmp/Moebius&lt;/code&gt; folder, the level above all of the research materials I had prepared for it. I prompted:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;Read ./moebius-web/research.md - your goal is to port this model to ONNX and WebGPU so we can run it directly in a browser, with a simple UI&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;As it started to work I dropped in this follow-up (typos included):&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;Bulid this in /tmp/Moebius/moebius-web and commit early and often, also maintain a notes.md file in there with notes about what you figure out along the way - also start by writing out a plan.md in there and update that plan as oy work too&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I often ask agents to keep notes like this - the end result is often interesting, both for myself and for the next agent session that touches the same project. Here's what that &lt;a href="https://github.com/simonw/moebius-web/blob/main/notes.md"&gt;notes.md file&lt;/a&gt; looked like at the end of the project.&lt;/p&gt;
&lt;p&gt;I kicked it off and went back to my main project, checking in occasionally to see how Claude was doing. When it looked like it might have something that worked I prompted:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;Tell me what URL I can visit in my own browser to try this&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Then I tried it out in Chrome and pasted some errors (and screenshots of errors) back into Claude Code.&lt;/p&gt;
&lt;p&gt;After a few rounds of this we had something that appeared to work! Time to put it on the internet so other people could use it.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;How would we publish this to Hugging Face such that the model weights were on there and the HTML demo would show up in Hugging Face spaces?&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Claude Code knows how to use the &lt;code&gt;hf&lt;/code&gt; CLI tool, so I created a model repo on &lt;a href="https://huggingface.co/"&gt;Hugging Face&lt;/a&gt;, then &lt;a href="https://huggingface.co/settings/tokens"&gt;created a token&lt;/a&gt; that could write to that repo and dropped it into a &lt;code&gt;/tmp/Moebius/token.txt&lt;/code&gt; file so Claude could use it.&lt;/p&gt;
&lt;p&gt;It published the 1.24GB of converted ONNX weights to &lt;a href="https://huggingface.co/simonw/Moebius-ONNX"&gt;huggingface.co/simonw/Moebius-ONNX&lt;/a&gt; for me.&lt;/p&gt;
&lt;p&gt;I'd seen other demos load weights into the browser from Hugging Face before, so I knew it was possible. I decided to host my own frontend code on GitHub Pages, so I said:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;I want to publish the moebius-web folder to GitHub, minus the large files (so maybe minus the models/ folder), such that when I turn on GitHub Pages for that repo navigating to https://simonw.github.io/moebius-web/ serves the UI&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Telling it the final URL was important in case it needed to fix the URLs in the demos that it was building so they would work when deployed to production.&lt;/p&gt;
&lt;p&gt;After a few more rounds of iteration, in between working on my main project, we got to a working, deployed version!&lt;/p&gt;
&lt;p&gt;Except... each time I reloaded the page it seemed to download ~1.3GB of model weights. Browser caching seemed pretty important for this!&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;anything clever we can do with serviceworkers or similar to help cache this stuff? It seems to reload every time, I am concerned that there might be something weird about the way HF redirects work that mean we don't benefit from browser caching&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I knew that Transformers.js projects could handle this properly, so I grabbed a copy of the &lt;a href="https://huggingface.co/spaces/Xenova/whisper-web"&gt;Whisper Web&lt;/a&gt; demo, dropped it into &lt;code&gt;/tmp/Moebius/whisper-web&lt;/code&gt; and said:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;look in /tmp/Moebius/whisper-web (with a subagent) and see how they do this&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That project was entirely obfuscated, built JavaScript files so I figured using a subagent would avoid spending the rest of my top-level token context deciphering those files.&lt;/p&gt;
&lt;p&gt;Claude figured out that it was using &lt;code&gt;caches.open("transformers-cache")&lt;/code&gt; - the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/CacheStorage/open"&gt;CacheStorage API&lt;/a&gt; - and &lt;a href="https://github.com/simonw/moebius-web/commit/05c1cbc4894460a70a8bc1718ac6d152219e0f28#diff-fb89c342dfa36f544a2d16a885b0f3d1d49f436a7d0eaeb80505f80a1f922603"&gt;added that to our project&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I've shared the &lt;a href="https://gisthost.github.io/?58039ba5c1ca3ed177e8659168996ee4"&gt;full Claude Code transcript&lt;/a&gt; for this project (published using my &lt;a href="https://github.com/simonw/claude-code-transcripts"&gt;claude-code-transcripts&lt;/a&gt; tool).&lt;/p&gt;
&lt;h4 id="what-did-i-learn-from-all-of-this-"&gt;What did I learn from all of this?&lt;/h4&gt;
&lt;p&gt;This definitely counts as vibe coding: I didn't look at a single line of code from the project, restricting my input to testing, suggesting small feature improvements (like a progress bar for the large file downloads) and pointing the model in the direction of examples of how I wanted things to work.&lt;/p&gt;
&lt;p&gt;Since I didn't write any code the amount I learned about the underlying technologies - WebGPU, ONNX, and the Moebius model itself - was very limited.&lt;/p&gt;
&lt;p&gt;As is usually the case with this kind of project the most important things I learned concerned what was &lt;em&gt;possible&lt;/em&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Claude Opus 4.8 is capable of converting a PyTorch model to ONNX, publishing the result to Hugging Face and then building out a web application and interface that can load and execute that model.&lt;/li&gt;
&lt;li&gt;Chrome, Firefox and Safari are all now capable of running this kind of model - I tried it in all three.&lt;/li&gt;
&lt;li&gt;The CacheStorage API works with ~1.3GB model files.&lt;/li&gt;
&lt;li&gt;... which means we can have inpainting as a feature of a client-only web application! (If our users can tolerate the 1.3GB download.)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I felt like I should probably try and learn a little more about my project. I fired up &lt;a href="https://claude.ai/"&gt;Claude.ai&lt;/a&gt; and prompted:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;Clone https://github.com/simonw/moebius-web/ and use it to teach me all about the model and ONNX and the process of converting a model to ONNX and WebGPU and basically everything I'd need to know in order to fully understand this repo&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Here's &lt;a href="https://claude.ai/share/d11b8f2b-a52d-4ca2-be75-a710eaf18572"&gt;the transcript&lt;/a&gt; and the &lt;a href="https://github.com/simonw/moebius-web/blob/main/understanding.md"&gt;understanding.md&lt;/a&gt; Markdown file it created, which I've now added to the GitHub repo. I found the explanation of ONNX particularly enlightening:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;ONNX&lt;/strong&gt; (Open Neural Network Exchange) is a portable, framework-neutral file format for neural networks. An &lt;code&gt;.onnx&lt;/code&gt; file is essentially two things bundled together:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;A computation graph&lt;/strong&gt; — a directed graph of &lt;em&gt;nodes&lt;/em&gt;, where each node is an &lt;strong&gt;operator&lt;/strong&gt; (&lt;code&gt;Conv&lt;/code&gt;, &lt;code&gt;MatMul&lt;/code&gt;, &lt;code&gt;Add&lt;/code&gt;, &lt;code&gt;Einsum&lt;/code&gt;, &lt;code&gt;Softmax&lt;/code&gt;, &lt;code&gt;Gather&lt;/code&gt;, &lt;code&gt;Resize&lt;/code&gt;, …) wired together by named tensors flowing between them. This is the "recipe" for the forward pass.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The weights&lt;/strong&gt; — the learned parameter tensors (the convolution kernels, the embedding table, etc.), stored as initializers in that same graph.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Crucially, ONNX describes &lt;em&gt;what to compute&lt;/em&gt;, abstractly, without saying &lt;em&gt;how&lt;/em&gt; or &lt;em&gt;on what hardware&lt;/em&gt;. The operator set is versioned by an &lt;strong&gt;opset&lt;/strong&gt; number (this repo uses &lt;strong&gt;opset 18&lt;/strong&gt;), which pins down exactly which operators exist and what their semantics are.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It turns out PyTorch has built in mechanisms for exporting to ONNX, as seen &lt;a href="https://github.com/simonw/moebius-web/blob/080be6e737ec976130e260d34707d7d9b7f63d5b/python/export_onnx.py#L91"&gt;here in export_onnx.py&lt;/a&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;span class="pl-s1"&gt;torch&lt;/span&gt;.&lt;span class="pl-c1"&gt;onnx&lt;/span&gt;.&lt;span class="pl-c1"&gt;export&lt;/span&gt;(
    &lt;span class="pl-s1"&gt;dec&lt;/span&gt;, (&lt;span class="pl-s1"&gt;lat&lt;/span&gt;,), &lt;span class="pl-s1"&gt;dec_path&lt;/span&gt;, &lt;span class="pl-s1"&gt;opset_version&lt;/span&gt;&lt;span class="pl-c1"&gt;=&lt;/span&gt;&lt;span class="pl-s1"&gt;args&lt;/span&gt;.&lt;span class="pl-c1"&gt;opset&lt;/span&gt;,
    &lt;span class="pl-s1"&gt;input_names&lt;/span&gt;&lt;span class="pl-c1"&gt;=&lt;/span&gt;[&lt;span class="pl-s"&gt;"latent"&lt;/span&gt;], &lt;span class="pl-s1"&gt;output_names&lt;/span&gt;&lt;span class="pl-c1"&gt;=&lt;/span&gt;[&lt;span class="pl-s"&gt;"image"&lt;/span&gt;],
    &lt;span class="pl-s1"&gt;dynamic_axes&lt;/span&gt;&lt;span class="pl-c1"&gt;=&lt;/span&gt;{&lt;span class="pl-s"&gt;"latent"&lt;/span&gt;: {&lt;span class="pl-c1"&gt;0&lt;/span&gt;: &lt;span class="pl-s"&gt;"B"&lt;/span&gt;}, &lt;span class="pl-s"&gt;"image"&lt;/span&gt;: {&lt;span class="pl-c1"&gt;0&lt;/span&gt;: &lt;span class="pl-s"&gt;"B"&lt;/span&gt;}},
)&lt;/pre&gt;
&lt;p&gt;Claude also included a &lt;a href="https://github.com/simonw/moebius-web/blob/main/understanding.md#12-mini-glossary"&gt;handy glossary&lt;/a&gt; and an only-slightly-broken &lt;a href="https://github.com/simonw/moebius-web/blob/main/understanding.md#10-putting-the-whole-pipeline-in-one-picture"&gt;ASCII-art diagram&lt;/a&gt; showing how the model pipeline fits together.&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/browsers"&gt;browsers&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/transformers-js"&gt;transformers-js&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/webgl"&gt;webgl&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/vibe-coding"&gt;vibe-coding&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/coding-agents"&gt;coding-agents&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/claude-code"&gt;claude-code&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/onnx"&gt;onnx&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="browsers"/><category term="transformers-js"/><category term="webgl"/><category term="vibe-coding"/><category term="coding-agents"/><category term="claude-code"/><category term="onnx"/></entry><entry><title>QR code generator</title><link href="https://simonwillison.net/2026/May/15/qr-code-generator/" rel="alternate"/><published>2026-05-15T04:00:49+00:00</published><updated>2026-05-15T04:00:49+00:00</updated><id>https://simonwillison.net/2026/May/15/qr-code-generator/</id><summary type="html">
    
        &lt;p&gt;&lt;strong&gt;Tool:&lt;/strong&gt; &lt;a href="https://tools.simonwillison.net/qr-code-generator"&gt;QR code generator&lt;/a&gt;&lt;/p&gt;
        &lt;p&gt;Claude helped me build this tool for creating QR codes, for both text/URLs and for connecting to WiFi networks.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Screenshot of a QR code generator web form. Heading &amp;quot;QR code generator&amp;quot; with subtitle &amp;quot;Create a scannable code for a URL, text, or WiFi network.&amp;quot; A segmented toggle shows &amp;quot;URL / text&amp;quot; and &amp;quot;WiFi&amp;quot; with WiFi selected. Below are fields: &amp;quot;Network name (SSID)&amp;quot; with placeholder &amp;quot;My WiFi&amp;quot;; &amp;quot;Password&amp;quot; with placeholder &amp;quot;Password&amp;quot; and a blue &amp;quot;Show&amp;quot; link; &amp;quot;Security&amp;quot; dropdown set to &amp;quot;WPA / WPA2 / WPA3 (most common)&amp;quot;; an unchecked &amp;quot;Hidden&amp;quot; checkbox; helper text &amp;quot;Not sure? Leave it on WPA / WPA2 / WPA3 — that covers almost every home WiFi network.&amp;quot; Below that: &amp;quot;Style&amp;quot; dropdown set to &amp;quot;Square&amp;quot;, an unchecked &amp;quot;Border&amp;quot; checkbox, &amp;quot;Size&amp;quot; dropdown set to &amp;quot;Medium&amp;quot;, and a &amp;quot;Color&amp;quot; swatch showing black. At the bottom is a blue &amp;quot;Generate QR code&amp;quot; button." src="https://static.simonwillison.net/static/2026/qr-code-generate.jpg" /&gt;&lt;/p&gt;
    
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/tools"&gt;tools&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai"&gt;ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/generative-ai"&gt;generative-ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/llms"&gt;llms&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/vibe-coding"&gt;vibe-coding&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="tools"/><category term="ai"/><category term="generative-ai"/><category term="llms"/><category term="vibe-coding"/></entry><entry><title>Big Words</title><link href="https://simonwillison.net/2026/May/7/big-words/" rel="alternate"/><published>2026-05-07T18:47:09+00:00</published><updated>2026-05-07T18:47:09+00:00</updated><id>https://simonwillison.net/2026/May/7/big-words/</id><summary type="html">
    
        &lt;p&gt;&lt;strong&gt;Tool:&lt;/strong&gt; &lt;a href="https://tools.simonwillison.net/big-words"&gt;Big Words&lt;/a&gt;&lt;/p&gt;
        &lt;p&gt;I'm using my &lt;a href="https://simonwillison.net/2026/Feb/25/present/"&gt;vibe coded macOS presentations tool&lt;/a&gt; to put together a talk, and I wanted to add a slide with some text on it. The tool only accepts URLs, so I &lt;a href="https://github.com/simonw/tools/pull/279"&gt;put together&lt;/a&gt; a quick page that accepts query string arguments and turns them into a simple slide.&lt;/p&gt;
&lt;p&gt;Here's an example: &lt;a href="https://tools.simonwillison.net/big-words?text=simonwillison.net&amp;amp;gradient=1&amp;amp;size=9.5"&gt;https://tools.simonwillison.net/big-words?text=simonwillison.net&amp;amp;gradient=1&amp;amp;size=9.5&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Double click or double tap the page to access a form for modifying the different options.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Screenshot of a slide editing tool showing a slide on the left with &amp;quot;simonwillison.net&amp;quot; in heavy white sans-serif text on a black-to-blue gradient background, and a &amp;quot;Slide settings&amp;quot; panel on the right with: TEXT field containing &amp;quot;simonwillison.net&amp;quot;, TEXT COLOR white, BACKGROUND black, &amp;quot;Use gradient background&amp;quot; checked, SECOND COLOR blue, ANGLE 135°, FONT &amp;quot;System sans-seri&amp;quot;, WEIGHT &amp;quot;Heavy&amp;quot;, SIZE 9.5vmin, unchecked Italic / Uppercase / Drop shadow checkboxes, and Reset and Save URL buttons." src="https://static.simonwillison.net/static/2026/big-words.jpg" /&gt;&lt;/p&gt;
    
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/tools"&gt;tools&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/vibe-coding"&gt;vibe-coding&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="tools"/><category term="vibe-coding"/></entry><entry><title>Vibe coding and agentic engineering are getting closer than I'd like</title><link href="https://simonwillison.net/2026/May/6/vibe-coding-and-agentic-engineering/" rel="alternate"/><published>2026-05-06T14:24:08+00:00</published><updated>2026-05-06T14:24:08+00:00</updated><id>https://simonwillison.net/2026/May/6/vibe-coding-and-agentic-engineering/</id><summary type="html">
    &lt;p&gt;I recently talked with Joseph Ruscio about AI coding tools for Heavybit's High Leverage podcast: &lt;a href="https://www.heavybit.com/library/podcasts/high-leverage/ep-9-the-ai-coding-paradigm-shift-with-simon-willison"&gt;Ep. #9, The AI Coding Paradigm Shift with Simon Willison&lt;/a&gt;. Here are some of my highlights, including my disturbing realization that vibe coding and agentic engineering have started to converge in my own work.&lt;/p&gt;
&lt;p&gt;One thing I really enjoy about podcasts is that they sometimes push me to think out loud in a way that exposes an idea I've not previously been able to put into words.&lt;/p&gt;
&lt;h4 id="vibe-coding-and-agentic-engineering-are-starting-to-overlap"&gt;Vibe coding and agentic engineering are starting to overlap&lt;/h4&gt;
&lt;p&gt;A few weeks after vibe coding was first coined I published &lt;a href="https://simonwillison.net/2025/Mar/19/vibe-coding/"&gt;Not all AI-assisted programming is vibe coding (but vibe coding rocks)&lt;/a&gt;, where I firmly staked out my belief that "vibe coding" is a very different beast from responsible use of AI to write code, which I've since started to call &lt;a href="https://simonwillison.net/guides/agentic-engineering-patterns/what-is-agentic-engineering/"&gt;agentic engineering&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;When Joseph brought up the distinction between the two I had a sudden realization that they're not nearly as distinct for me as they used to be:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Weirdly though, those things have started to blur for me already, which is quite upsetting.&lt;/p&gt;
&lt;p&gt;I thought we had a very clear delineation where vibe coding is the thing where you're not looking at the code at all. You might not even know how to program. You might be a non-programmer who asks for a thing, and gets a thing, and if the thing works, then great! And if it doesn't, you tell it that it doesn't work and cross your fingers.&lt;/p&gt;
&lt;p&gt;But at no point are you really caring about the code quality or any of those additional constraints. And my take on vibe coding was that it's fantastic, provided you understand when it can be used and when it can't.&lt;/p&gt;
&lt;p&gt;A personal tool for you, where if there's a bug it hurts only you, go ahead!&lt;/p&gt;
&lt;p&gt;If you're building software for other people, vibe coding is grossly irresponsible because it's other people's information. Other people get hurt by your stupid bugs. You need to have a higher level than that.&lt;/p&gt;
&lt;p&gt;This contrasts with agentic engineering where you are a professional software engineer. You understand security and maintainability and operations and performance and so forth. You're using these tools to the highest of your own ability. I'm finding the scope of challenges I can take on has gone up by a significant amount because I've got the support of these tools.&lt;/p&gt;
&lt;p&gt;But I'm still leaning on my 25 years of experience as a software engineer.&lt;/p&gt;
&lt;p&gt;The goal is to build high quality production systems: if you're building lower quality stuff faster, I think that's bad. I want to build &lt;em&gt;higher&lt;/em&gt; quality stuff faster. I want everything I'm building to be better in every way than it was before.&lt;/p&gt;
&lt;p&gt;The problem is that as the coding agents get more reliable, I'm not reviewing every line of code that they write anymore, even for my production level stuff.&lt;/p&gt;
&lt;p&gt;I know full well that if you ask Claude Code to build a JSON API endpoint that runs a SQL query and outputs the results as JSON, it's just going to do it right. It's not going to mess that up. You have it add automated tests, you have it add documentation, you know it's going to be good.&lt;/p&gt;
&lt;p&gt;But I'm not reviewing that code. And now I've got that feeling of guilt: if I haven't reviewed the code, is it really responsible for me to use this in production?&lt;/p&gt;
&lt;p&gt;The thing that really helps me is thinking back to when I've worked at larger organizations where I've been an engineering manager. Other teams are building software that my team depends on.&lt;/p&gt;
&lt;p&gt;If another team hands over something and says, "hey, this is the image resize service, here's how to use it to resize your images"... I'm not going to go and read every line of code that they wrote.&lt;/p&gt;
&lt;p&gt;I'm going to look at their documentation and I'm going to use it to resize some images. And then I'm going to start shipping my own features. And if I start running into problems where the image resizer thing appears to have bugs or the performance isn't good, that's when I might dig into their Git repositories and see what's going on. But for the most part I treat that as a semi-black box that I don't look at until I need to.&lt;/p&gt;
&lt;p&gt;I'm starting to treat the agents in the same way. And it still feels uncomfortable, because human beings are accountable for what they do. A team can build a reputation. I can say "I trust that team over there. They built good software in the past. They're not going to build something rubbish because that affects their professional reputations."&lt;/p&gt;
&lt;p&gt;Claude Code does not have a professional reputation! It can't take accountability for what it's done. But it's been proving itself anyway - time and time again it's churning out straightforward things and doing them right in the style that I like.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;There's an element of &lt;a href="https://simonwillison.net/2025/Dec/10/normalization-of-deviance/"&gt;the normalization of deviance&lt;/a&gt; here - every time a model turns out to have written the right code without me monitoring it closely there's a risk that I'll trust it at the wrong moment in the future and get burned.&lt;/p&gt;
&lt;h4 id="the-new-challenge-of-evaluating-software"&gt;The new challenge of evaluating software&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;It used to be if you found a GitHub repository with a hundred commits and a good readme and automated tests and stuff, you could be pretty sure that the person writing that had put a lot of care and attention into that project.&lt;/p&gt;
&lt;p&gt;And now I can knock out a git repository with a hundred commits and a beautiful readme and comprehensive tests of every line of code in half an hour! It looks identical to those projects that have had a great deal of care and attention. Maybe it is as good as them. I don't know. I can't tell from looking at it. Even for my &lt;em&gt;own&lt;/em&gt; projects, I can't tell.&lt;/p&gt;
&lt;p&gt;So I realized what I value more than the quality of the tests and documentation is that I want somebody to have &lt;em&gt;used&lt;/em&gt; the thing. If you've got a vibe coded thing which you have used every day for the past two weeks, that's much more valuable to me than something that you've just spat out and hardly even exercised.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id="the-bottlenecks-have-shifted"&gt;The bottlenecks have shifted&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;If you can go from producing 200 lines of code a day to 2,000 lines of code a day, what else breaks? The entire software development lifecycle was, it turns out, designed around the idea that it takes a day to produce a few hundred lines of code. And now it doesn't.&lt;/p&gt;
&lt;p&gt;It's not just the downstream stuff, it's the upstream stuff as well. I saw &lt;a href="https://simonwillison.net/2026/Jan/24/dont-trust-the-process/"&gt;a great talk by Jenny Wen&lt;/a&gt;, who's the design leader at Anthropic, where she said we have all of these design processes that are based around the idea that you need to get the design &lt;em&gt;right&lt;/em&gt; - because if you hand it off to the engineers and they spend three months building the wrong thing, that's catastrophic.&lt;/p&gt;
&lt;p&gt;There's this whole very extensive design process that you put in place because that design results in expensive work. But if it doesn't take three months to build, maybe the design process can be a whole lot riskier because cost, if you get something wrong, has been reduced so much.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id="why-i-m-still-not-afraid-for-my-career"&gt;Why I'm still not afraid for my career&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;When I look at my conversations with the agents, it's very clear to me that this is moon language for the vast majority of human beings.&lt;/p&gt;
&lt;p&gt;There are a whole bunch of reasons I'm not scared that my career as a software engineer is over now that computers can write their own code, partly because these things are amplifiers of existing experience. If you know what you're doing, you can run so much faster with them. [...]&lt;/p&gt;
&lt;p&gt;I'm constantly reminded as I work with these tools how hard the thing that we do is. Producing software is a &lt;em&gt;ferociously&lt;/em&gt; difficult thing to do. And you could give me all of the AI tools in the world and what we're trying to achieve here is still really difficult. [...]&lt;/p&gt;
&lt;p&gt;Matthew Yglesias, who's a political commentator, yesterday &lt;a href="https://twitter.com/mattyglesias/status/2049105745132585161"&gt;tweeted&lt;/a&gt;, "Five months in, I think I've decided that I don't want to vibecode — I want professionally managed software companies to use AI coding assistance to make more/better/cheaper software products that they sell to me for money." And that feels about right to me. I can plumb my house if I watch enough YouTube videos on plumbing. I would rather hire a plumber.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;On the threat to SaaS providers of companies rolling their own solutions instead:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I just realized it's the thing I said earlier about how I only want to use your side project if you've used it for a few weeks. The enterprise version of that is I don't want a CRM unless at least two other giant enterprises have successfully used that CRM for six months. [...] You want solutions that are proven to work before you take a risk on them.&lt;/p&gt;
&lt;/blockquote&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/ai"&gt;ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/generative-ai"&gt;generative-ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/llms"&gt;llms&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/podcast-appearances"&gt;podcast-appearances&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/vibe-coding"&gt;vibe-coding&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/coding-agents"&gt;coding-agents&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/agentic-engineering"&gt;agentic-engineering&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="ai"/><category term="generative-ai"/><category term="llms"/><category term="podcast-appearances"/><category term="vibe-coding"/><category term="coding-agents"/><category term="agentic-engineering"/></entry><entry><title>We need RSS for sharing abundant vibe-coded apps</title><link href="https://simonwillison.net/2026/Apr/30/rss-vibe-coded-apps/" rel="alternate"/><published>2026-04-30T18:38:48+00:00</published><updated>2026-04-30T18:38:48+00:00</updated><id>https://simonwillison.net/2026/Apr/30/rss-vibe-coded-apps/</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://interconnected.org/home/2026/04/29/syndicating-vibes"&gt;We need RSS for sharing abundant vibe-coded apps&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Matt Webb:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I would love an RSS web feed for all those various tools and apps pages, each item with an “Install” button. (But install to where?)&lt;/p&gt;
&lt;p&gt;The lesson here is that when vibe-coding accelerates app development, apps become more personal, more situated, and more frequent. Shipping a tool or a micro-app is less like launching a website and more like posting on a blog.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This inspired me to &lt;a href="https://github.com/simonw/simonwillisonblog/pull/665"&gt;have Claude&lt;/a&gt; add an Atom feed (and icon) to my &lt;a href="https://simonwillison.net/elsewhere/tool/"&gt;/elsewhere/tools/&lt;/a&gt; page, which itself is populated by content from my &lt;a href="https://tools.simonwillison.net/"&gt;tools.simonwillison.net&lt;/a&gt; site.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/atom"&gt;atom&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/matt-webb"&gt;matt-webb&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/rss"&gt;rss&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai"&gt;ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/vibe-coding"&gt;vibe-coding&lt;/a&gt;&lt;/p&gt;



</summary><category term="atom"/><category term="matt-webb"/><category term="rss"/><category term="ai"/><category term="vibe-coding"/></entry><entry><title>Quoting Matthew Yglesias</title><link href="https://simonwillison.net/2026/Apr/28/matthew-yglesias/" rel="alternate"/><published>2026-04-28T13:25:29+00:00</published><updated>2026-04-28T13:25:29+00:00</updated><id>https://simonwillison.net/2026/Apr/28/matthew-yglesias/</id><summary type="html">
    &lt;blockquote cite="https://twitter.com/mattyglesias/status/2049105745132585161"&gt;&lt;p&gt;Five months in, I think I've decided that I don't want to vibecode — I want professionally managed software companies to use AI coding assistance to make more/better/cheaper software products that they sell to me for money.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p class="cite"&gt;&amp;mdash; &lt;a href="https://twitter.com/mattyglesias/status/2049105745132585161"&gt;Matthew Yglesias&lt;/a&gt;, in a now-deleted Tweet&lt;/p&gt;

    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/agentic-engineering"&gt;agentic-engineering&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/vibe-coding"&gt;vibe-coding&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai-assisted-programming"&gt;ai-assisted-programming&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai"&gt;ai&lt;/a&gt;&lt;/p&gt;



</summary><category term="agentic-engineering"/><category term="vibe-coding"/><category term="ai-assisted-programming"/><category term="ai"/></entry><entry><title>Extract PDF text in your browser with LiteParse for the web</title><link href="https://simonwillison.net/2026/Apr/23/liteparse-for-the-web/" rel="alternate"/><published>2026-04-23T21:54:24+00:00</published><updated>2026-04-23T21:54:24+00:00</updated><id>https://simonwillison.net/2026/Apr/23/liteparse-for-the-web/</id><summary type="html">
    &lt;p&gt;LlamaIndex have a most excellent open source project called &lt;a href="https://github.com/run-llama/liteparse"&gt;LiteParse&lt;/a&gt;, which provides a Node.js CLI tool for extracting text from PDFs. I got a version of LiteParse working entirely in the browser, using most of the same libraries that LiteParse uses to run in Node.js.&lt;/p&gt;
&lt;h4 id="spatial-text-parsing"&gt;Spatial text parsing&lt;/h4&gt;
&lt;p&gt;Refreshingly, LiteParse doesn't use AI models to do what it does: it's good old-fashioned PDF parsing, falling back to Tesseract OCR (or other pluggable OCR engines) for PDFs that contain images of text rather than the text itself.&lt;/p&gt;
&lt;p&gt;The hard problem that LiteParse solves is extracting text in a sensible order despite the infuriating vagaries of PDF layouts. They describe this as "spatial text parsing" - they use some very clever heuristics to detect things like multi-column layouts and group and return the text in a sensible linear flow.&lt;/p&gt;
&lt;p&gt;The LiteParse documentation describes a pattern for implementing &lt;a href="https://developers.llamaindex.ai/liteparse/guides/visual-citations/"&gt;Visual Citations with Bounding Boxes&lt;/a&gt;. I really like this idea: being able to answer questions from a PDF and accompany those answers with cropped, highlighted images feels like a great way of increasing the credibility of answers from RAG-style Q&amp;amp;A.&lt;/p&gt;
&lt;p&gt;LiteParse is provided as a pure CLI tool, designed to be used by agents. You run it like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npm i -g @llamaindex/liteparse
lit parse document.pdf
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I &lt;a href="https://claude.ai/share/44a5ed86-e5b5-4e14-90be-1eba1e0acd13"&gt;explored its capabilities with Claude&lt;/a&gt; and quickly determined that there was no real reason it had to stay a CLI app: it's built on top of PDF.js and Tesseract.js, two libraries I've used for something similar in a browser &lt;a href="https://simonwillison.net/2024/Mar/30/ocr-pdfs-images/"&gt;in the past&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The only reason LiteParse didn't have a pure browser-based version is that nobody had built one yet...&lt;/p&gt;
&lt;h4 id="introducing-liteparse-for-the-web"&gt;Introducing LiteParse for the web&lt;/h4&gt;
&lt;p&gt;Visit &lt;a href="https://simonw.github.io/liteparse/"&gt;https://simonw.github.io/liteparse/&lt;/a&gt; to try out LiteParse against any PDF file, running entirely in your browser. Here's what that looks like:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://static.simonwillison.net/static/2026/liteparse-web.jpg" alt="Screenshot of the LiteParse browser demo web page. Header reads &amp;quot;LiteParse&amp;quot; with subtitle &amp;quot;Browser demo of LiteParse — parse PDFs in your browser. Nothing leaves your machine.&amp;quot; A dashed-border drop zone says &amp;quot;Drop a PDF here or click to choose / Your file stays in your browser.&amp;quot; with a file pill labeled &amp;quot;19720005243.pdf&amp;quot;. Below are a checked &amp;quot;Run OCR&amp;quot; checkbox, an unchecked &amp;quot;Render page screenshots&amp;quot; checkbox, and a blue &amp;quot;Parse&amp;quot; button. Status text: &amp;quot;Parsed 86 pages.&amp;quot; Two side-by-side panels follow. Left panel titled &amp;quot;Text&amp;quot; with a Copy button shows monospace extracted text beginning &amp;quot;Apollo 5 was an unmanned system, both propulsion systems ascent and descent stages&amp;quot;. Right panel titled &amp;quot;JSON&amp;quot;, also with a copy button, contains JSON showing the dimensions and position and detected font of each piece of text." style="max-width: 100%;" /&gt;&lt;/p&gt;
&lt;p&gt;The tool can work with or without running OCR, and can optionally display images for every page in the PDF further down the page.&lt;/p&gt;
&lt;h4 id="building-it-with-claude-code-and-opus-4-7"&gt;Building it with Claude Code and Opus 4.7&lt;/h4&gt;
&lt;p&gt;The process of building this started in the regular Claude app on my iPhone. I wanted to try out LiteParse myself, so I started by uploading a random PDF I happened to have on my phone along with this prompt:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;Clone https://github.com/run-llama/liteparse and try it against this file&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Regular Claude chat can clone directly from GitHub these days, and while by default it can't access most of the internet from its container it can also install packages from PyPI and npm.&lt;/p&gt;
&lt;p&gt;I often use this to try out new pieces of open source software on my phone - it's a quick way to exercise something without having to sit down with my laptop.&lt;/p&gt;
&lt;p&gt;You can follow my full conversation in &lt;a href="https://claude.ai/share/44a5ed86-e5b5-4e14-90be-1eba1e0acd13"&gt;this shared Claude transcript&lt;/a&gt;. I asked a few follow-up questions about how it worked, and then asked:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;Does this library run in a browser? Could it?&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This gave me a thorough enough answer that I was convinced it was worth trying getting that to work for real. I opened up my laptop and switched to Claude Code.&lt;/p&gt;
&lt;p&gt;I forked the original repo on GitHub, cloned a local copy, started a new &lt;code&gt;web&lt;/code&gt; branch and pasted that last reply from Claude into a new file called &lt;a href="https://github.com/simonw/liteparse/blob/web/notes.md"&gt;notes.md&lt;/a&gt;. Then I told Claude Code:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;Get this working as a web app. index.html, when loaded, should render an app that lets users open a PDF in their browser and select OCR or non-OCR mode and have this run. Read notes.md for initial research on this problem, then write out plan.md with your detailed implementation plan&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I always like to start with a plan for this kind of project. Sometimes I'll use Claude's "planning mode", but in this case I knew I'd want the plan as an artifact in the repository so I told it to write &lt;code&gt;plan.md&lt;/code&gt; directly.&lt;/p&gt;
&lt;p&gt;This also means I can iterate on the plan with Claude. I noticed that Claude had decided to punt on generating screenshots of images in the PDF, and suggested we defer a "canvas-encode swap" to v2. I fixed that by prompting:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;Update the plan to say we WILL do the canvas-encode swap so the screenshots thing works&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;After a few short follow-up prompts, here's the &lt;a href="https://github.com/simonw/liteparse/blob/web/plan.md"&gt;plan.md&lt;/a&gt; I thought was strong enough to implement.&lt;/p&gt;
&lt;p&gt;I prompted:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;build it.&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;And then mostly left Claude Code to its own devices, tinkered with some other projects, caught up on Duolingo and occasionally checked in to see how it was doing.&lt;/p&gt;
&lt;p&gt;I added a few prompts to the queue as I was working. Those don't yet show up in my exported transcript, but it turns out running &lt;code&gt;rg queue-operation --no-filename | grep enqueue | jq -r '.content'&lt;/code&gt; in the relevant &lt;code&gt;~/.claude/projects/&lt;/code&gt; folder extracts them.&lt;/p&gt;
&lt;p&gt;Here are the key follow-up prompts with some notes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;When you implement this use playwright and red/green TDD, plan that too&lt;/code&gt; - I've written more &lt;a href="https://simonwillison.net/guides/agentic-engineering-patterns/red-green-tdd/"&gt;about red/green TDD here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;let's use PDF.js's own renderer&lt;/code&gt; (it was messing around with pdfium)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;The final UI should include both the text and the pretty-printed JSON output, both of those in textareas and both with copy-to-clipboard buttons - it should also be mobile friendly&lt;/code&gt; - I had a new idea for how the UI should work&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;small commits along the way&lt;/code&gt; - see below&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Make sure the index.html page includes a link back to https://github.com/run-llama/liteparse near the top of the page&lt;/code&gt; - it's important to credit your dependencies in a project like this!&lt;/li&gt;
&lt;li&gt;&lt;code&gt;View on GitHub → is bad copy because that's not the repo with this web app in, it's the web app for the underlying LiteParse library&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Run OCR should be unchecked by default&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;When I try to parse a PDF in my browser I see 'Parse failed: undefined is not a function (near '...value of readableStream...')&lt;/code&gt; - it was testing with Playwright in Chrome, turned out there was a bug in Safari&lt;/li&gt;
&lt;li&gt;&lt;code&gt;... oh that is in safari but it works in chrome&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;When "Copy" is clicked the text should change to "Copied!" for 1.5s&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;[Image #1] Style the file input so that long filenames don't break things on Firefox like this - in fact add one of those drag-drop zone UIs which you can also click to select a file&lt;/code&gt; - dropping screenshots in of small UI glitches works surprisingly well&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Tweak the drop zone such that the text is vertically centered, right now it is a bit closer to the top&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;it breaks in Safari on macOS, works in both Chrome and Firefox. On Safari I see "Parse failed: undefined is not a function (near '...value of readableStream...')" after I click the Parse button, when OCR is not checked&lt;/code&gt; - it still wasn't working in Safari...&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;works in safari now&lt;/code&gt;  - but it fixed it pretty quickly once I pointed that out and it got Playwright working with that browser&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I've started habitually asking for "small commits along the way" because it makes for code that's easier to understand or review later on, and I have an unproven hunch that it helps the agent work more effectively too - it's yet another encouragement towards planning and taking on one problem at a time.&lt;/p&gt;
&lt;p&gt;While it was working I decided it would be nice to be able to interact with an in-progress version.  I asked a separate Claude Code session against the same directory for tips on how to run it, and it told me to use &lt;code&gt;npx vite&lt;/code&gt;. Running that started a development server with live-reloading, which meant I could instantly see the effect of each change it made on disk - and prompt with further requests for tweaks and fixes.&lt;/p&gt;
&lt;p&gt;Towards the end I decided it was going to be good enough to publish. I started a fresh Claude Code instance and told it:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;Look at the web/ folder - set up GitHub actions for this repo such that any push runs the tests, and if the tests pass it then does a GitHub Pages deploy of the built vite app such that the web/index.html page is the index.html page for the thing that is deployed and it works on GitHub Pages&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;After a bit more iteration &lt;a href="https://github.com/simonw/liteparse/blob/web/.github/workflows/deploy-web.yml"&gt;here's the GitHub Actions workflow&lt;/a&gt; that builds the app using Vite and deploys the result to &lt;a href="https://simonw.github.io/liteparse/"&gt;https://simonw.github.io/liteparse/&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I love GitHub Pages for this kind of thing because it can be quickly configured (by Claude, in this case) to turn any repository into a deployed web-app, at zero cost and with whatever build step is necessary. It even works against private repos, if you don't mind your only security being a secret URL.&lt;/p&gt;
&lt;p&gt;With this kind of project there's always a major risk that the model might "cheat" - mark key features as "TODO" and fake them, or take shortcuts that ignore the initial requirements.&lt;/p&gt;
&lt;p&gt;The responsible way to prevent this is to review all of the code... but this wasn't intended as that kind of project, so instead I fired up OpenAI Codex with GPT-5.5 (I had preview access) and told it:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;Describe the difference between how the node.js CLI tool runs and how the web/ version runs&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The answer I got back was enough to give me confidence that Claude hadn't taken any project-threatening shortcuts.&lt;/p&gt;
&lt;p&gt;... and that was about it. Total time in Claude Code for that "build it" step was 59 minutes. I used my &lt;a href="https://github.com/simonw/claude-code-transcripts"&gt;claude-code-transcripts&lt;/a&gt; tool to export a readable version of the full transcript which you can &lt;a href="https://gisthost.github.io/?d64889bfc1b897fea3867adfec62ed89/index.html"&gt;view here&lt;/a&gt;, albeit without those additional queued prompts (here's my &lt;a href="https://github.com/simonw/claude-code-transcripts/issues/98"&gt;issue to fix that&lt;/a&gt;).&lt;/p&gt;
&lt;h4 id="is-this-even-vibe-coding-any-more-"&gt;Is this even vibe coding any more?&lt;/h4&gt;
&lt;p&gt;I'm a pedantic stickler when it comes to &lt;a href="https://simonwillison.net/2025/Mar/19/vibe-coding/"&gt;the original definition of vibe coding&lt;/a&gt; - vibe coding does &lt;em&gt;not&lt;/em&gt; mean any time you use AI to help you write code, it's when you use AI without reviewing or caring about the code that's written at all.&lt;/p&gt;
&lt;p&gt;By my own definition, this LiteParse for the web project is about as pure vibe coding as you can get! I have not looked at a &lt;em&gt;single line&lt;/em&gt; of the HTML and TypeScript written for this project - in fact while writing this sentence I had to go and check if it had used JavaScript or TypeScript.&lt;/p&gt;
&lt;p&gt;Yet somehow this one doesn't feel as vibe coded to me as many of my other vibe coded projects:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;As a static in-browser web application hosted on GitHub Pages the blast radius for any bugs is almost non-existent: it either works for your PDF or doesn't.&lt;/li&gt;
&lt;li&gt;No private data is transferred anywhere - all processing happens in your browser - so a security audit is unnecessary. I've glanced once at the network panel while it's running and no additional requests are made when a PDF is being parsed.&lt;/li&gt;
&lt;li&gt;There was still a whole lot of engineering experience and knowledge required to use the models in this way. Identifying that porting LiteParse to run directly in a browser was critical to the rest of the project.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Most importantly, I'm happy to attach my reputation to this project and recommend that other people try it out. Unlike most of my vibe coded tools I'm not convinced that spending significant additional engineering time on this would have resulted in a meaningfully better initial release. It's fine as it is!&lt;/p&gt;
&lt;p&gt;I haven't opened a PR against the &lt;a href="https://github.com/run-llama/liteparse"&gt;origin repository&lt;/a&gt; because I've not discussed it with the LiteParse team. I've &lt;a href="https://github.com/run-llama/liteparse/issues/147"&gt;opened an issue&lt;/a&gt;, and if they want my vibe coded implementation as a starting point for something more official they're welcome to take it.&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/javascript"&gt;javascript&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ocr"&gt;ocr&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/pdf"&gt;pdf&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/projects"&gt;projects&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai"&gt;ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/generative-ai"&gt;generative-ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/llms"&gt;llms&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/vibe-coding"&gt;vibe-coding&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/coding-agents"&gt;coding-agents&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/claude-code"&gt;claude-code&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/agentic-engineering"&gt;agentic-engineering&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="javascript"/><category term="ocr"/><category term="pdf"/><category term="projects"/><category term="ai"/><category term="generative-ai"/><category term="llms"/><category term="vibe-coding"/><category term="coding-agents"/><category term="claude-code"/><category term="agentic-engineering"/></entry><entry><title>datasette.io news preview</title><link href="https://simonwillison.net/2026/Apr/16/datasette-io-preview/" rel="alternate"/><published>2026-04-16T00:18:03+00:00</published><updated>2026-04-16T00:18:03+00:00</updated><id>https://simonwillison.net/2026/Apr/16/datasette-io-preview/</id><summary type="html">
    
        &lt;p&gt;&lt;strong&gt;Tool:&lt;/strong&gt; &lt;a href="https://tools.simonwillison.net/datasette-io-preview"&gt;datasette.io news preview&lt;/a&gt;&lt;/p&gt;
        &lt;p&gt;The &lt;a href="https://datasette.io/"&gt;datasette.io&lt;/a&gt; website has a news section built from this &lt;a href="https://github.com/simonw/datasette.io/blob/main/news.yaml"&gt;news.yaml&lt;/a&gt; file in the underlying GitHub repository. The YAML format looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;- date: 2026-04-15
  body: |-
    [Datasette 1.0a27](https://docs.datasette.io/en/latest/changelog.html#a27-2026-04-15) changes how CSRF protection works in a way that simplifies form and API integration, and introduces a new `RenameTableEvent` for when a table is renamed by a SQL query.
- date: 2026-03-18
  body: |-
    ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This format is a little hard to edit, so I finally &lt;a href="https://claude.ai/share/c96129b9-bcb0-4eba-aee9-4a7ad236dfb7"&gt;had Claude build a custom preview UI&lt;/a&gt; to make checking for errors have slightly less friction.&lt;/p&gt;
&lt;p&gt;I built it using standard &lt;a href="https://claude.ai/"&gt;claude.ai&lt;/a&gt; and Claude Artifacts, taking advantage of Claude's ability to clone GitHub repos and look at their content as part of a regular chat:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;Clone https://github.com/simonw/datasette.io and look at the news.yaml file and how it is rendered on the homepage. Build an artifact I can paste that YAML into which previews what it will look like, and highlights any markdown errors or YAML errors&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img alt="Screenshot showing two side-by-side views of a datasette.io news preview tool. The left panel shows a dark-themed YAML editor with news entries containing date and body fields in Markdown format, with a red validation error at the bottom indicating the date field has an invalid format. The right panel shows the rendered preview output with formatted headings by date (April 2026, 18th March 2026), displaying 115 news entries with linked release names, inline code snippets, and changelog descriptions. A red badge with &amp;quot;1&amp;quot; appears on the left panel header indicating one validation error." src="https://static.simonwillison.net/static/2026/datasette-io-preview.jpg" /&gt;&lt;/p&gt;
    
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/tools"&gt;tools&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/yaml"&gt;yaml&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/datasette"&gt;datasette&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/claude"&gt;claude&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/vibe-coding"&gt;vibe-coding&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="tools"/><category term="yaml"/><category term="datasette"/><category term="claude"/><category term="vibe-coding"/></entry><entry><title>Gemini 3.1 Flash TTS</title><link href="https://simonwillison.net/2026/Apr/15/gemini-31-flash-tts/" rel="alternate"/><published>2026-04-15T17:13:14+00:00</published><updated>2026-04-15T17:13:14+00:00</updated><id>https://simonwillison.net/2026/Apr/15/gemini-31-flash-tts/</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://blog.google/innovation-and-ai/models-and-research/gemini-models/gemini-3-1-flash-tts/"&gt;Gemini 3.1 Flash TTS&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Google released Gemini 3.1 Flash TTS today, a new text-to-speech model that can be directed using prompts.&lt;/p&gt;
&lt;p&gt;It's presented via the standard Gemini API using &lt;code&gt;gemini-3.1-flash-tts-preview&lt;/code&gt; as the model ID, but can only output audio files.&lt;/p&gt;
&lt;p&gt;The &lt;a href="https://ai.google.dev/gemini-api/docs/speech-generation#transcript-tags"&gt;prompting guide&lt;/a&gt; is surprising, to say the least. Here's their example prompt to generate just a few short sentences of audio:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# AUDIO PROFILE: Jaz R.
## "The Morning Hype"

## THE SCENE: The London Studio
It is 10:00 PM in a glass-walled studio overlooking the moonlit London skyline, but inside, it is blindingly bright. The red "ON AIR" tally light is blazing. Jaz is standing up, not sitting, bouncing on the balls of their heels to the rhythm of a thumping backing track. Their hands fly across the faders on a massive mixing desk. It is a chaotic, caffeine-fueled cockpit designed to wake up an entire nation.

### DIRECTOR'S NOTES
Style:
* The "Vocal Smile": You must hear the grin in the audio. The soft palate is always raised to keep the tone bright, sunny, and explicitly inviting.
* Dynamics: High projection without shouting. Punchy consonants and elongated vowels on excitement words (e.g., "Beauuutiful morning").

Pace: Speaks at an energetic pace, keeping up with the fast music.  Speaks with A "bouncing" cadence. High-speed delivery with fluid transitions — no dead air, no gaps.

Accent: Jaz is from Brixton, London

### SAMPLE CONTEXT
Jaz is the industry standard for Top 40 radio, high-octane event promos, or any script that requires a charismatic Estuary accent and 11/10 infectious energy.

#### TRANSCRIPT
[excitedly] Yes, massive vibes in the studio! You are locked in and it is absolutely popping off in London right now. If you're stuck on the tube, or just sat there pretending to work... stop it. Seriously, I see you.
[shouting] Turn this up! We've got the project roadmap landing in three, two... let's go!
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here's what I got using that example prompt:&lt;/p&gt;
&lt;p&gt;&lt;audio controls style="width: 100%"&gt;
  &lt;source src="https://static.simonwillison.net/static/2026/gemini-flash-tts-london.wav" type="audio/wav"&gt;
  Your browser does not support the audio element.
&lt;/audio&gt;&lt;/p&gt;
&lt;p&gt;Then I modified it to say "Jaz is from Newcastle" and "... requires a charismatic Newcastle accent" and got this result:&lt;/p&gt;
&lt;p&gt;&lt;audio controls style="width: 100%"&gt;
  &lt;source src="https://static.simonwillison.net/static/2026/gemini-flash-tts-newcastle.wav" type="audio/wav"&gt;
  Your browser does not support the audio element.
&lt;/audio&gt;&lt;/p&gt;
&lt;p&gt;Here's Exeter, Devon for good measure:&lt;/p&gt;
&lt;p&gt;&lt;audio controls style="width: 100%"&gt;
  &lt;source src="https://static.simonwillison.net/static/2026/gemini-flash-tts-devon.wav" type="audio/wav"&gt;
  Your browser does not support the audio element.
&lt;/audio&gt;&lt;/p&gt;
&lt;p&gt;I &lt;a href="https://gemini.google.com/share/dd0fba5a83c4"&gt;had Gemini 3.1 Pro&lt;/a&gt; vibe code &lt;a href="https://tools.simonwillison.net/gemini-flash-tts"&gt;this UI for trying it out&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Screenshot of a &amp;quot;Gemini 3.1 Flash TTS&amp;quot; web application interface. At the top is an &amp;quot;API Key&amp;quot; field with a masked password. Below is a &amp;quot;TTS Mode&amp;quot; section with a dropdown set to &amp;quot;Multi-Speaker (Conversation)&amp;quot;. &amp;quot;Speaker 1 Name&amp;quot; is set to &amp;quot;Joe&amp;quot; with &amp;quot;Speaker 1 Voice&amp;quot; set to &amp;quot;Puck (Upbeat)&amp;quot;. &amp;quot;Speaker 2 Name&amp;quot; is set to &amp;quot;Jane&amp;quot; with &amp;quot;Speaker 2 Voice&amp;quot; set to &amp;quot;Kore (Firm)&amp;quot;. Under &amp;quot;Script / Prompt&amp;quot; is a tip reading &amp;quot;Tip: Format your text as a script using the Exact Speaker Names defined above.&amp;quot; The script text area contains &amp;quot;TTS the following conversation between Joe and Jane:\n\nJoe: How's it going today Jane?\nJane: [yawn] Not too bad, how about you?&amp;quot; A blue &amp;quot;Generate Audio&amp;quot; button is below. At the bottom is a &amp;quot;Success!&amp;quot; message with an audio player showing 00:00 / 00:06 and a &amp;quot;Download WAV&amp;quot; link." src="https://static.simonwillison.net/static/2026/gemini-flash-tts.jpg" /&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/google"&gt;google&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/text-to-speech"&gt;text-to-speech&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/tools"&gt;tools&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai"&gt;ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/prompt-engineering"&gt;prompt-engineering&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/generative-ai"&gt;generative-ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/llms"&gt;llms&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/gemini"&gt;gemini&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/llm-release"&gt;llm-release&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/vibe-coding"&gt;vibe-coding&lt;/a&gt;&lt;/p&gt;



</summary><category term="google"/><category term="text-to-speech"/><category term="tools"/><category term="ai"/><category term="prompt-engineering"/><category term="generative-ai"/><category term="llms"/><category term="gemini"/><category term="llm-release"/><category term="vibe-coding"/></entry><entry><title>Cybersecurity Looks Like Proof of Work Now</title><link href="https://simonwillison.net/2026/Apr/14/cybersecurity-proof-of-work/" rel="alternate"/><published>2026-04-14T19:41:48+00:00</published><updated>2026-04-14T19:41:48+00:00</updated><id>https://simonwillison.net/2026/Apr/14/cybersecurity-proof-of-work/</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.dbreunig.com/2026/04/14/cybersecurity-is-proof-of-work-now.html"&gt;Cybersecurity Looks Like Proof of Work Now&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
The UK's AI Safety Institute recently published &lt;a href="https://www.aisi.gov.uk/blog/our-evaluation-of-claude-mythos-previews-cyber-capabilities"&gt;Our evaluation of Claude Mythos Preview’s cyber capabilities&lt;/a&gt;, their own independent analysis of &lt;a href="https://simonwillison.net/2026/Apr/7/project-glasswing/"&gt;Claude Mythos&lt;/a&gt; which backs up Anthropic's claims that it is exceptionally effective at identifying security vulnerabilities.&lt;/p&gt;
&lt;p&gt;Drew Breunig notes that AISI's report shows that the more tokens (and hence money) they spent the better the result they got, which leads to a strong economic incentive to spend as much as possible on security reviews:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If Mythos continues to find exploits so long as you keep throwing money at it, security is reduced to a brutally simple equation: &lt;strong&gt;to harden a system you need to spend more tokens discovering exploits than attackers will spend exploiting them&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;An interesting result of this is that open source libraries become &lt;em&gt;more&lt;/em&gt; valuable, since the tokens spent securing them can be shared across all of their users. This directly counters the idea that the low cost of vibe-coding up a replacement for an open source library makes those open source projects less attractive.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/open-source"&gt;open-source&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai"&gt;ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/generative-ai"&gt;generative-ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/llms"&gt;llms&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/drew-breunig"&gt;drew-breunig&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/vibe-coding"&gt;vibe-coding&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai-security-research"&gt;ai-security-research&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/claude-mythos-fable"&gt;claude-mythos-fable&lt;/a&gt;&lt;/p&gt;



</summary><category term="open-source"/><category term="ai"/><category term="generative-ai"/><category term="llms"/><category term="drew-breunig"/><category term="vibe-coding"/><category term="ai-security-research"/><category term="claude-mythos-fable"/></entry><entry><title>Eight years of wanting, three months of building with AI</title><link href="https://simonwillison.net/2026/Apr/5/building-with-ai/" rel="alternate"/><published>2026-04-05T23:54:18+00:00</published><updated>2026-04-05T23:54:18+00:00</updated><id>https://simonwillison.net/2026/Apr/5/building-with-ai/</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://lalitm.com/post/building-syntaqlite-ai/"&gt;Eight years of wanting, three months of building with AI&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Lalit Maganti provides one of my favorite pieces of long-form writing on agentic engineering I've seen in ages.&lt;/p&gt;
&lt;p&gt;They spent eight years thinking about and then three months building &lt;a href="https://github.com/lalitMaganti/syntaqlite"&gt;syntaqlite&lt;/a&gt;, which they describe as "&lt;a href="https://lalitm.com/post/syntaqlite/"&gt;high-fidelity devtools that SQLite deserves&lt;/a&gt;".&lt;/p&gt;
&lt;p&gt;The goal was to provide fast, robust and comprehensive linting and verifying tools for SQLite, suitable for use in language servers and other development tools - a parser, formatter, and verifier for SQLite queries. I've found myself wanting this kind of thing in the past myself, hence my (far less production-ready) &lt;a href="https://simonwillison.net/2026/Jan/30/sqlite-ast-2/"&gt;sqlite-ast&lt;/a&gt; project from a few months ago.&lt;/p&gt;
&lt;p&gt;Lalit had been procrastinating on this project for years, because of the inevitable tedium of needing to work through 400+ grammar rules to help build a parser. That's exactly the kind of tedious work that coding agents excel at!&lt;/p&gt;
&lt;p&gt;Claude Code helped get over that initial hump and build the first prototype:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;AI basically let me put aside all my doubts on technical calls, my uncertainty of building the right thing and my reluctance to get started by giving me very concrete problems to work on. Instead of “I need to understand how SQLite’s parsing works”, it was “I need to get AI to suggest an approach for me so I can tear it up and build something better". I work so much better with concrete prototypes to play with and code to look at than endlessly thinking about designs in my head, and AI lets me get to that point at a pace I could not have dreamed about before. Once I took the first step, every step after that was so much easier.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That first vibe-coded prototype worked great as a proof of concept, but they eventually made the decision to throw it away and start again from scratch. AI worked great for the low level details but did not produce a coherent high-level architecture:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I found that AI made me procrastinate on key design decisions. Because refactoring was cheap, I could always say “I’ll deal with this later.” And because AI could refactor at the same industrial scale it generated code, the cost of deferring felt low. But it wasn’t: deferring decisions corroded my ability to think clearly because the codebase stayed confusing in the meantime.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The second attempt took a lot longer and involved a great deal more human-in-the-loop decision making, but the result is a robust library that can stand the test of time.&lt;/p&gt;
&lt;p&gt;It's worth setting aside some time to read this whole thing - it's full of non-obvious downsides to working heavily with AI, as well as a detailed explanation of how they overcame those hurdles.&lt;/p&gt;
&lt;p&gt;The key idea I took away from this concerns AI's weakness in terms of design and architecture:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;When I was working on something where I didn’t even know what I wanted, AI was somewhere between unhelpful and harmful. The architecture of the project was the clearest case: I spent weeks in the early days following AI down dead ends, exploring designs that felt productive in the moment but collapsed under scrutiny. In hindsight, I have to wonder if it would have been faster just thinking it through without AI in the loop at all.&lt;/p&gt;
&lt;p&gt;But expertise alone isn’t enough. Even when I understood a problem deeply, AI still struggled if the task had no objectively checkable answer. Implementation has a right answer, at least at a local level: the code compiles, the tests pass, the output matches what you asked for. Design doesn’t. We’re still arguing about OOP decades after it first took off.&lt;/p&gt;
&lt;/blockquote&gt;

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://news.ycombinator.com/item?id=47648828"&gt;Hacker News&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/sqlite"&gt;sqlite&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai"&gt;ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/generative-ai"&gt;generative-ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/llms"&gt;llms&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai-assisted-programming"&gt;ai-assisted-programming&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/vibe-coding"&gt;vibe-coding&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/agentic-engineering"&gt;agentic-engineering&lt;/a&gt;&lt;/p&gt;



</summary><category term="sqlite"/><category term="ai"/><category term="generative-ai"/><category term="llms"/><category term="ai-assisted-programming"/><category term="vibe-coding"/><category term="agentic-engineering"/></entry><entry><title>Python Vulnerability Lookup</title><link href="https://simonwillison.net/2026/Mar/29/python-vulnerability-lookup/" rel="alternate"/><published>2026-03-29T18:46:16+00:00</published><updated>2026-03-29T18:46:16+00:00</updated><id>https://simonwillison.net/2026/Mar/29/python-vulnerability-lookup/</id><summary type="html">
    
        &lt;p&gt;&lt;strong&gt;Tool:&lt;/strong&gt; &lt;a href="https://tools.simonwillison.net/python-vulnerability-lookup"&gt;Python Vulnerability Lookup&lt;/a&gt;&lt;/p&gt;
        &lt;p&gt;I learned that the &lt;a href="https://osv.dev/"&gt;OSV.dev&lt;/a&gt; open source vulnerability database has an open CORS &lt;a href="https://google.github.io/osv.dev/api/"&gt;JSON API&lt;/a&gt;, so I had Claude Code build this &lt;a href="https://simonwillison.net/2025/Dec/10/html-tools/"&gt;HTML tool&lt;/a&gt; for pasting in a &lt;code&gt;pyproject.toml&lt;/code&gt; or &lt;code&gt;requirements.txt&lt;/code&gt; file (or name of a GitHub repo containing those) and seeing a list of all reported vulnerabilities from that API.&lt;/p&gt;
    
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/security"&gt;security&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/tools"&gt;tools&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/supply-chain"&gt;supply-chain&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/vibe-coding"&gt;vibe-coding&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="python"/><category term="security"/><category term="tools"/><category term="supply-chain"/><category term="vibe-coding"/></entry><entry><title>Quoting Matt Webb</title><link href="https://simonwillison.net/2026/Mar/28/matt-webb/" rel="alternate"/><published>2026-03-28T12:04:26+00:00</published><updated>2026-03-28T12:04:26+00:00</updated><id>https://simonwillison.net/2026/Mar/28/matt-webb/</id><summary type="html">
    &lt;blockquote cite="https://interconnected.org/home/2026/03/28/architecture"&gt;&lt;p&gt;The thing about agentic coding is that agents grind problems into dust. Give an agent a problem and a while loop and - long term - it’ll solve that problem even if it means burning a trillion tokens and re-writing down to the silicon. [...]&lt;/p&gt;
&lt;p&gt;But we want AI agents to solve coding problems quickly and in a way that is maintainable and adaptive and composable (benefiting from improvements elsewhere), and where every addition makes the whole stack better.&lt;/p&gt;
&lt;p&gt;So at the bottom is really great libraries that encapsulate hard problems, with great interfaces that make the “right” way the easy way for developers building apps with them. Architecture!&lt;/p&gt;
&lt;p&gt;While I’m vibing (I call it vibing now, not coding and not vibe coding) while I’m vibing, I am looking at lines of code less than ever before, and thinking about architecture more than ever before.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p class="cite"&gt;&amp;mdash; &lt;a href="https://interconnected.org/home/2026/03/28/architecture"&gt;Matt Webb&lt;/a&gt;, An appreciation for (technical) architecture&lt;/p&gt;

    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/matt-webb"&gt;matt-webb&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai"&gt;ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/llms"&gt;llms&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/vibe-coding"&gt;vibe-coding&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/coding-agents"&gt;coding-agents&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai-assisted-programming"&gt;ai-assisted-programming&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/generative-ai"&gt;generative-ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/agentic-engineering"&gt;agentic-engineering&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/definitions"&gt;definitions&lt;/a&gt;&lt;/p&gt;



</summary><category term="matt-webb"/><category term="ai"/><category term="llms"/><category term="vibe-coding"/><category term="coding-agents"/><category term="ai-assisted-programming"/><category term="generative-ai"/><category term="agentic-engineering"/><category term="definitions"/></entry><entry><title>Vibe coding SwiftUI apps is a lot of fun</title><link href="https://simonwillison.net/2026/Mar/27/vibe-coding-swiftui/" rel="alternate"/><published>2026-03-27T20:59:53+00:00</published><updated>2026-03-27T20:59:53+00:00</updated><id>https://simonwillison.net/2026/Mar/27/vibe-coding-swiftui/</id><summary type="html">
    &lt;p&gt;I have a new laptop - a 128GB M5 MacBook Pro, which early impressions show to be &lt;em&gt;very&lt;/em&gt; capable for running good local LLMs. I got frustrated with Activity Monitor and decided to vibe code up some alternative tools for monitoring performance and I'm very happy with the results.&lt;/p&gt;
&lt;p&gt;This is my second experiment with vibe coding macOS apps - the first was &lt;a href="https://simonwillison.net/2026/Feb/25/present/"&gt;this presentation app a few weeks ago&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;It turns out Claude Opus 4.6 and GPT-5.4 are both very competent at SwiftUI - and a full SwiftUI app can fit in a single text file, which means I can use them to spin something up without even opening Xcode.&lt;/p&gt;
&lt;p&gt;I’ve built two apps so far: Bandwidther shows me what apps are using network bandwidth and Gpuer to show me what’s going on with the GPU. At Claude’s suggestion both of these are now menu bar icons that open a panel full of information.&lt;/p&gt;
&lt;h4 id="bandwidther"&gt;Bandwidther&lt;/h4&gt;
&lt;p&gt;I built this app first, because I wanted to see what Dropbox was doing. It looks like this:&lt;/p&gt;
&lt;p&gt;&lt;a target="_blank" rel="noopener noreferrer" href="https://github.com/simonw/bandwidther/raw/main/screenshot.png"&gt;&lt;img src="https://github.com/simonw/bandwidther/raw/main/screenshot.png" alt="Screenshot of Bandwidther macOS app showing two columns: left side displays overall download/upload speeds, a bandwidth graph over the last 60 seconds, cumulative totals, internet and LAN connection counts, and internet destinations; right side shows per-process bandwidth usage sorted by rate with processes like nsurlsessiond, apsd, rapportd, mDNSResponder, Dropbox, and others listed with their individual download/upload speeds and progress bars." style="max-width: 100%;" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I’ve shared &lt;a href="https://gisthost.github.io/?6e06d4724c64c10d1fc3fbe19d9c8575/index.html"&gt;the full transcript&lt;/a&gt; I used to build the first version of the app. My prompts were pretty minimal:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Show me how much network bandwidth is in use from this machine to the internet as opposed to local LAN&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;(My initial curiosity was to see if Dropbox was transferring files via the LAN from my old computer or was downloading from the internet.)&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;mkdir /tmp/bandwidther and write a native Swift UI app in there that shows me these details on a live ongoing basis&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This got me the first version, which proved to me this was worth pursuing further.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;git init and git commit what you have so far&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Since I was about to start adding new features.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Now suggest features we could add to that app, the goal is to provide as much detail as possible concerning network usage including by different apps&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The nice thing about having Claude suggest features is that it has a much better idea for what’s possible than I do.&lt;/p&gt;
&lt;p&gt;We had a bit of back and forth fixing some bugs, then I sent a few more prompts to get to the two column layout shown above:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;add Per-Process Bandwidth, relaunch the app once that is done&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;now add the reverse DNS feature but make sure original IP addresses are still visible too, albeit in smaller typeface&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;redesign the app so that it is wider, I want two columns - the per-process one on the left and the rest on the right&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;OK make it a task bar icon thing, when I click the icon I want the app to appear, the icon itself should be a neat minimal little thing&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The source code and build instructions are available in &lt;a href="https://github.com/simonw/bandwidther"&gt;simonw/bandwidther&lt;/a&gt;.&lt;/p&gt;
&lt;h4 id="gpuer"&gt;Gpuer&lt;/h4&gt;
&lt;p&gt;While I was building Bandwidther in one session I had another session running to build a similar tool for seeing what the GPU was doing. Here’s what I ended up with:&lt;/p&gt;
&lt;p&gt;&lt;a target="_blank" rel="noopener noreferrer" href="https://github.com/simonw/gpuer/raw/main/screenshot.png"&gt;&lt;img src="https://github.com/simonw/gpuer/raw/main/screenshot.png" alt="Screenshot of the Gpuer app on macOS showing memory usage for an Apple M5 Max with 40 GPU cores. Left panel: a large orange &amp;quot;38 GB Available&amp;quot; readout showing usage of 128.0 GB unified memory, &amp;quot;Room for ~18 more large apps before pressure&amp;quot;, a warning banner reading &amp;quot;1.5 GB pushed to disk — system was under pressure recently&amp;quot;, a horizontal segmented bar chart labeled &amp;quot;Where your memory is going&amp;quot; with green, blue, and grey segments and a legend, an explanatory note about GPU unified memory, a GPU Utilization section showing 0%, and a History graph showing Available and GPU Utilization over time as line charts. Right panel: a Memory Footprint list sorted by Memory, showing process names with horizontal pink/purple usage bars and CPU percentage labels beside each entry, covering processes including Dropbox, WebKit, Virtualization, node, Claude Helper, Safari, LM Studio, WindowServer, Finder, and others." style="max-width: 100%;" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Here's &lt;a href="https://gisthost.github.io/?71ffe216ceca8d7da59a07c478d17529"&gt;the transcript&lt;/a&gt;. This one took even less prompting because I could use the in-progress Bandwidther as an example:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I want to know how much RAM and GPU this computer is using, which is hard because stuff on the GPU and RAM does not seem to show up in Activity Monitor&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This collected information using &lt;code&gt;system_profiler&lt;/code&gt; and &lt;code&gt;memory_pressure&lt;/code&gt; and gave me &lt;a href="https://gisthost.github.io/?71ffe216ceca8d7da59a07c478d17529/page-001.html#msg-2026-03-24T22-13-26-614Z"&gt;an answer&lt;/a&gt; - more importantly it showed me this was possible, so I said:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Look at /tmp/bandwidther and then create a similar app in /tmp/gpuer which shows the information from above on an ongoing basis, or maybe does it better&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;After a few more changes to the Bandwidther app I told it to catch up:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Now take a look at recent changes in /tmp/bandwidther - that app now uses a sys tray icon, imitate that&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This remains one of my favorite tricks for using coding agents: having them &lt;a href="https://simonwillison.net/guides/agentic-engineering-patterns/hoard-things-you-know-how-to-do/#recombining-things-from-your-hoard"&gt;recombine elements&lt;/a&gt; from other projects.&lt;/p&gt;
&lt;p&gt;The code for Gpuer can be found in &lt;a href="https://github.com/simonw/gpuer"&gt;simonw/gpuer&lt;/a&gt; on GitHub.&lt;/p&gt;
&lt;h4 id="you-shouldn-t-trust-these-apps"&gt;You shouldn't trust these apps&lt;/h4&gt;
&lt;p&gt;These two apps are classic vibe coding: I don't know Swift and I hardly glanced at the code they were writing.&lt;/p&gt;
&lt;p&gt;More importantly though, I have very little experience with macOS internals such as the values these tools are measuring. I am completely unqualified to evaluate if the numbers and charts being spat out by these tools are credible or accurate!&lt;/p&gt;
&lt;p&gt;I've added warnings to both GitHub repositories to that effect.&lt;/p&gt;
&lt;p&gt;This morning I caught Gpuer reporting that I had just 5GB of memory left when that clearly wasn't the case (according to Activity Monitor). I &lt;a href="https://gisthost.github.io/?9ae12fff0fecc9a4482c9b02e8599c70/page-001.html#msg-2026-03-27T19-35-35-866Z"&gt;pasted a screenshot into Claude Code&lt;/a&gt; and it &lt;a href="https://github.com/simonw/gpuer/commit/a3cd655f5ccb274d3561e4cbfcc771b0bb7e256a"&gt;adjusted the calculations&lt;/a&gt; and the new numbers &lt;em&gt;look&lt;/em&gt; right, but I'm still not confident that it's reporting things correctly.&lt;/p&gt;
&lt;p&gt;I only shared them on GitHub because I think they're interesting as an example of what Claude can do with SwiftUI.&lt;/p&gt;
&lt;p&gt;Despite my lack of confidence in the apps themselves, I did learn some useful things from these projects:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A SwiftUI app can get a whole lot done with a single file of code - here's &lt;a href="https://github.com/simonw/gpuer/blob/main/GpuerApp.swift"&gt;GpuerApp.swift&lt;/a&gt; (880 lines) and &lt;a href="https://github.com/simonw/bandwidther/blob/main/BandwidtherApp.swift"&gt;BandwidtherApp.swift&lt;/a&gt; (1063 lines).&lt;/li&gt;
&lt;li&gt;Wrapping various terminal commands in a neat UI with Swift is easily achieved.&lt;/li&gt;
&lt;li&gt;Claude has surprisingly good design taste when it comes to SwiftUI applications.&lt;/li&gt;
&lt;li&gt;Turning an app into a menu bar app is just a few lines of extra code as well.&lt;/li&gt;
&lt;li&gt;You don't need to open Xcode to build this kind of application!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These two apps took very little time to build and have convinced me that building macOS apps in SwiftUI is a new capability I should consider for future projects.&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/macos"&gt;macos&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai"&gt;ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/generative-ai"&gt;generative-ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/llms"&gt;llms&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/vibe-coding"&gt;vibe-coding&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/coding-agents"&gt;coding-agents&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/swift"&gt;swift&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/claude-code"&gt;claude-code&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="macos"/><category term="ai"/><category term="generative-ai"/><category term="llms"/><category term="vibe-coding"/><category term="coding-agents"/><category term="swift"/><category term="claude-code"/></entry><entry><title>Quoting Craig Mod</title><link href="https://simonwillison.net/2026/Mar/13/craig-mod/" rel="alternate"/><published>2026-03-13T17:14:29+00:00</published><updated>2026-03-13T17:14:29+00:00</updated><id>https://simonwillison.net/2026/Mar/13/craig-mod/</id><summary type="html">
    &lt;blockquote cite="https://craigmod.com/essays/software_bonkers/"&gt;&lt;p&gt;Simply put: It’s a big mess, and no off-the-shelf accounting software does what I need. So after years of pain, I finally sat down last week and started to build my own. It took me about five days. I am now using the best piece of accounting software I’ve ever used. It’s blazing fast. Entirely local. Handles multiple currencies and pulls daily (historical) conversion rates. It’s able to ingest any CSV I throw at it and represent it in my dashboard as needed. It knows US and Japan tax requirements, and formats my expenses and medical bills appropriately for my accountants. I feed it past returns to learn from. I dump 1099s and K1s and PDFs from hospitals into it, and it categorizes and organizes and packages them all as needed. It reconciles international wire transfers, taking into account small variations in FX rates and time for the transfers to complete. It learns as I categorize expenses and categorizes automatically going forward. It’s easy to do spot checks on data. If I find an anomaly, I can talk directly to Claude and have us brainstorm a batched solution, often saving me from having to manually modify hundreds of entries. And often resulting in a new, small, feature tweak. The software feels organic and pliable in a form perfectly shaped to my hand, able to conform to any hunk of data I throw at it. It feels like bushwhacking with a lightsaber.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p class="cite"&gt;&amp;mdash; &lt;a href="https://craigmod.com/essays/software_bonkers/"&gt;Craig Mod&lt;/a&gt;, Software Bonkers&lt;/p&gt;

    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/vibe-coding"&gt;vibe-coding&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai-assisted-programming"&gt;ai-assisted-programming&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/generative-ai"&gt;generative-ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai"&gt;ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/llms"&gt;llms&lt;/a&gt;&lt;/p&gt;



</summary><category term="vibe-coding"/><category term="ai-assisted-programming"/><category term="generative-ai"/><category term="ai"/><category term="llms"/></entry><entry><title>Sorting algorithms</title><link href="https://simonwillison.net/2026/Mar/11/sorting-algorithms/" rel="alternate"/><published>2026-03-11T22:58:06+00:00</published><updated>2026-03-11T22:58:06+00:00</updated><id>https://simonwillison.net/2026/Mar/11/sorting-algorithms/</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://tools.simonwillison.net/sort-algorithms"&gt;Sorting algorithms&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Today in animated explanations built using Claude: I've always been a fan of animated demonstrations of sorting algorithms so I decided to spin some up on my phone using Claude Artifacts, then added Python's timsort algorithm, then a feature to run them all at once. Here's the &lt;a href="https://claude.ai/share/2c09f6f7-57ed-47eb-af2e-fc39ddc4c39f"&gt;full sequence of prompts&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Interactive animated demos of the most common sorting algorithms&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This gave me bubble sort, selection sort, insertion sort, merge sort, quick sort, and heap sort.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Add timsort, look up details in a clone of python/cpython from GitHub&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Let's add Python's &lt;a href="https://en.wikipedia.org/wiki/Timsort"&gt;Timsort&lt;/a&gt;! Regular Claude chat can clone repos from GitHub these days. In the transcript you can see it clone the repo and then consult &lt;a href="https://github.com/python/cpython/blob/d19de375a204c74ab5f3a28ec42335bae139033d/Objects/listsort.txt"&gt;Objects/listsort.txt&lt;/a&gt; and &lt;a href="https://github.com/python/cpython/blob/d19de375a204c74ab5f3a28ec42335bae139033d/Objects/listobject.c"&gt;Objects/listobject.c&lt;/a&gt;. (I should note that when I asked GPT-5.4 Thinking to review Claude's implementation &lt;a href="https://chatgpt.com/share/69b1fc93-f360-8006-b8b7-22c3da639367"&gt;it picked holes in it&lt;/a&gt; and said the code "is a simplified, Timsort-inspired adaptive mergesort".)&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I don't like the dark color scheme on the buttons, do better&lt;/p&gt;
&lt;p&gt;Also add a "run all" button which shows smaller animated charts for every algorithm at once in a grid and runs them all at the same time&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It came up with a color scheme I liked better, "do better" is a fun prompt, and now the "Run all" button produces this effect:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Animated sorting algorithm race visualization titled &amp;quot;All algorithms racing&amp;quot; with controls for SIZE (50) and SPEED (100), Stop and Shuffle buttons, and a &amp;quot;Back to single&amp;quot; button. A legend shows Comparing (pink), Swapping (orange), Pivot (red), and Sorted (purple) indicators. Seven algorithms race simultaneously in card panels: Bubble sort (Sorting… — Comparisons: 312, Swaps: 250), Selection sort (Sorting… — Comparisons: 550, Swaps: 12), Insertion sort (Sorting… — Comparisons: 295, Swaps: 266), Merge sort (#3 — Comparisons: 225, Swaps: 225), Quick sort (#2 — Comparisons: 212, Swaps: 103), Heap sort (Sorting… — Comparisons: 358, Swaps: 203), and Timsort (#1 — Comparisons: 215, Swaps: 332). Finished algorithms (Timsort, Quick sort, Merge sort) display fully sorted purple bar charts and are highlighted with purple borders." src="https://static.simonwillison.net/static/2026/sorts-32-colors-lossy.gif" /&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/algorithms"&gt;algorithms&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/computer-science"&gt;computer-science&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/javascript"&gt;javascript&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/sorting"&gt;sorting&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai"&gt;ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/explorables"&gt;explorables&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/generative-ai"&gt;generative-ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/llms"&gt;llms&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/claude"&gt;claude&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/vibe-coding"&gt;vibe-coding&lt;/a&gt;&lt;/p&gt;



</summary><category term="algorithms"/><category term="computer-science"/><category term="javascript"/><category term="sorting"/><category term="ai"/><category term="explorables"/><category term="generative-ai"/><category term="llms"/><category term="claude"/><category term="vibe-coding"/></entry><entry><title>NICAR 2026 Schedule</title><link href="https://simonwillison.net/2026/Mar/4/nicar-2026/" rel="alternate"/><published>2026-03-04T21:34:26+00:00</published><updated>2026-03-04T21:34:26+00:00</updated><id>https://simonwillison.net/2026/Mar/4/nicar-2026/</id><summary type="html">
    
        &lt;p&gt;&lt;strong&gt;Tool:&lt;/strong&gt; &lt;a href="https://tools.simonwillison.net/nicar-2026"&gt;NICAR 2026 Schedule&lt;/a&gt;&lt;/p&gt;
        &lt;p&gt;The &lt;a href="https://www.ire.org/training/conferences/nicar-2026/"&gt;NICAR&lt;/a&gt; data journalism conference provides a copy of the schedule as CORS-enabled JSON, so I vibe coded my own mobile-friendly schedule app to help me keep track of the sessions I want to attend.&lt;/p&gt;
    
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/nicar"&gt;nicar&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/cors"&gt;cors&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/vibe-coding"&gt;vibe-coding&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="nicar"/><category term="cors"/><category term="vibe-coding"/></entry><entry><title>Unicode Explorer using binary search over fetch() HTTP range requests</title><link href="https://simonwillison.net/2026/Feb/27/unicode-explorer/" rel="alternate"/><published>2026-02-27T17:50:54+00:00</published><updated>2026-02-27T17:50:54+00:00</updated><id>https://simonwillison.net/2026/Feb/27/unicode-explorer/</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://tools.simonwillison.net/unicode-binary-search"&gt;Unicode Explorer using binary search over fetch() HTTP range requests&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Here's a little prototype I built this morning from my phone as an experiment in HTTP range requests, and a general example of using LLMs to satisfy curiosity.&lt;/p&gt;
&lt;p&gt;I've been collecting &lt;a href="https://simonwillison.net/tags/http-range-requests/"&gt;HTTP range tricks&lt;/a&gt; for a while now, and I decided it would be fun to build something with them myself that used binary search against a large file to do something useful.&lt;/p&gt;
&lt;p&gt;So I &lt;a href="https://claude.ai/share/47860666-cb20-44b5-8cdb-d0ebe363384f"&gt;brainstormed with Claude&lt;/a&gt;. The challenge was coming up with a use case for binary search where the data could be naturally sorted in a way that would benefit from binary search.&lt;/p&gt;
&lt;p&gt;One of Claude's suggestions was looking up information about unicode codepoints, which means searching through many MBs of metadata.&lt;/p&gt;
&lt;p&gt;I had Claude write me a spec to feed to Claude Code - &lt;a href="https://github.com/simonw/research/pull/90#issue-4001466642"&gt;visible here&lt;/a&gt; - then kicked off an &lt;a href="https://simonwillison.net/2025/Nov/6/async-code-research/"&gt;asynchronous research project&lt;/a&gt; with Claude Code for web against my &lt;a href="https://github.com/simonw/research"&gt;simonw/research&lt;/a&gt; repo to turn that into working code.&lt;/p&gt;
&lt;p&gt;Here's the &lt;a href="https://github.com/simonw/research/tree/main/unicode-explorer-binary-search#readme"&gt;resulting report and code&lt;/a&gt;. One interesting thing I learned is that Range request tricks aren't compatible with HTTP compression because they mess with the byte offset calculations. I added &lt;code&gt;'Accept-Encoding': 'identity'&lt;/code&gt; to the &lt;code&gt;fetch()&lt;/code&gt; calls but this isn't actually necessary because Cloudflare and other CDNs automatically skip compression if a &lt;code&gt;content-range&lt;/code&gt; header is present.&lt;/p&gt;
&lt;p&gt;I deployed the result &lt;a href="https://tools.simonwillison.net/unicode-binary-search"&gt;to my tools.simonwillison.net site&lt;/a&gt;, after first tweaking it to query the data via range requests against a CORS-enabled 76.6MB file in an S3 bucket fronted by Cloudflare.&lt;/p&gt;
&lt;p&gt;The demo is fun to play with - type in a single character like &lt;code&gt;ø&lt;/code&gt; or a hexadecimal codepoint indicator like &lt;code&gt;1F99C&lt;/code&gt; and it will binary search its way through the large file and show you the steps it takes along the way:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Animated demo of a web tool called Unicode Explore. I enter the ampersand character and hit Search. A box below shows a sequence of HTTP binary search requests made, finding in 17 steps with 3,864 bytes transferred and telling me that ampersand is U+0026 in Punctuation other, Basic Latin" src="https://static.simonwillison.net/static/2026/unicode-explore.gif" /&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/algorithms"&gt;algorithms&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/http"&gt;http&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/research"&gt;research&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/tools"&gt;tools&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/unicode"&gt;unicode&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai"&gt;ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/generative-ai"&gt;generative-ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/llms"&gt;llms&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai-assisted-programming"&gt;ai-assisted-programming&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/vibe-coding"&gt;vibe-coding&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/http-range-requests"&gt;http-range-requests&lt;/a&gt;&lt;/p&gt;



</summary><category term="algorithms"/><category term="http"/><category term="research"/><category term="tools"/><category term="unicode"/><category term="ai"/><category term="generative-ai"/><category term="llms"/><category term="ai-assisted-programming"/><category term="vibe-coding"/><category term="http-range-requests"/></entry><entry><title>I vibe coded my dream macOS presentation app</title><link href="https://simonwillison.net/2026/Feb/25/present/" rel="alternate"/><published>2026-02-25T16:46:19+00:00</published><updated>2026-02-25T16:46:19+00:00</updated><id>https://simonwillison.net/2026/Feb/25/present/</id><summary type="html">
    &lt;p&gt;I gave a talk this weekend at Social Science FOO Camp in Mountain View. The event was a classic unconference format where anyone could present a talk without needing to propose it in advance. I grabbed a slot for a talk I titled "The State of LLMs, February 2026 edition", subtitle "It's all changed since November!". I vibe coded a custom macOS app for the presentation the night before.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://static.simonwillison.net/static/2026/state-of-llms.jpg" alt="A sticky note on a board at FOO Camp. It reads: The state of LLMs, Feb 2026 edition - it's all changed since November! Simon Willison - the card is littered with names of new models: Qwen 3.5, DeepSeek 3.2, Sonnet 4.6, Kimi K2.5, GLM5, Opus 4.5/4.6, Gemini 3.1 Pro, Codex 5.3. The card next to it says Why do Social Scientists think they need genetics? Bill January (it's not all because of AI)" style="max-width: 100%;" /&gt;&lt;/p&gt;
&lt;p&gt;I've written about the last twelve months of development in LLMs in &lt;a href="https://simonwillison.net/2023/Dec/31/ai-in-2023/"&gt;December 2023&lt;/a&gt;, &lt;a href="https://simonwillison.net/2024/Dec/31/llms-in-2024/"&gt;December 2024&lt;/a&gt; and &lt;a href="https://simonwillison.net/2025/Dec/31/the-year-in-llms/"&gt;December 2025&lt;/a&gt;. I also presented &lt;a href="https://simonwillison.net/2025/Jun/6/six-months-in-llms/"&gt;The last six months in LLMs, illustrated by pelicans on bicycles&lt;/a&gt; at the AI Engineer World’s Fair in June 2025. This was my first time dropping the time covered to just three months, which neatly illustrates how much the space keeps accelerating and felt appropriate given the &lt;a href="https://simonwillison.net/2026/Jan/4/inflection/"&gt;November 2025 inflection point&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;(I further illustrated this acceleration by wearing a Gemini 3 sweater to the talk, which I was given a couple of weeks ago and is already out-of-date &lt;a href="https://simonwillison.net/2026/Feb/19/gemini-31-pro/"&gt;thanks to Gemini 3.1&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;I always like to have at least one gimmick in any talk I give, based on the STAR moment principle I &lt;a href="https://simonwillison.net/2019/Dec/10/better-presentations/"&gt;learned at Stanford&lt;/a&gt; - include Something They'll Always Remember to try and help your talk stand out.&lt;/p&gt;
&lt;p&gt;For this talk I had two gimmicks. I built the first part of the talk around coding agent assisted data analysis of Kākāpō breeding season (which meant I got to &lt;a href="https://simonwillison.net/2026/Feb/8/kakapo-mug/"&gt;show off my mug&lt;/a&gt;), then did a quick tour of some new pelicans riding bicycles before ending with the reveal that the entire presentation had been presented using a new macOS app I had vibe coded in ~45 minutes the night before the talk.&lt;/p&gt;
&lt;h4 id="present-app"&gt;Present.app&lt;/h4&gt;
&lt;p&gt;The app is called &lt;strong&gt;Present&lt;/strong&gt; - literally the first name I thought of. It's built using Swift and SwiftUI and weighs in at 355KB, or &lt;a href="https://github.com/simonw/present/releases/tag/0.1a0"&gt;76KB compressed&lt;/a&gt;. Swift apps are tiny!&lt;/p&gt;
&lt;p&gt;It may have been quick to build but the combined set of features is something I've wanted for &lt;em&gt;years&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;I usually use Keynote for presentations, but sometimes I like to mix things up by presenting using a sequence of web pages. I do this by loading up a browser window with a tab for each page, then clicking through those tabs in turn while I talk.&lt;/p&gt;
&lt;p&gt;This works great, but comes with a very scary disadvantage: if the browser crashes I've just lost my entire deck!&lt;/p&gt;
&lt;p&gt;I always have the URLs in a notes file, so I can click back to that and launch them all manually if I need to, but it's not something I'd like to deal with in the middle of a talk.&lt;/p&gt;
&lt;p&gt;This was &lt;a href="https://gisthost.github.io/?639d3c16dcece275af50f028b32480c7/page-001.html#msg-2026-02-21T05-53-43-395Z"&gt;my starting prompt&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Build a SwiftUI app for giving presentations where every slide is a URL. The app starts as a window with a webview on the right and a UI on the left for adding, removing and reordering the sequence of URLs. Then you click Play in a menu and the app goes full screen and the left and right keys switch between URLs&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That produced a plan. You can see &lt;a href="https://gisthost.github.io/?bfbc338977ceb71e298e4d4d5ac7d63c"&gt;the transcript that implemented that plan here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In Present a talk is an ordered sequence of URLs, with a sidebar UI for adding, removing and reordering those URLs. That's the entirety of the editing experience.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://static.simonwillison.net/static/2026/present.jpg" alt="Screenshot of a macOS app window titled &amp;quot;Present&amp;quot; showing Google Image search results for &amp;quot;kakapo&amp;quot;. A web view shows a Google image search with thumbnail photos of kākāpō parrots with captions. A sidebar on the left shows a numbered list of URLs, mostly from simonwillison.net and static.simonwillison.net, with item 4 (https://www.google.com/search?...) highlighted in blue." style="max-width: 100%;" /&gt;&lt;/p&gt;
&lt;p&gt;When you select the "Play" option in the menu (or hit Cmd+Shift+P) the app switches to full screen mode. Left and right arrow keys navigate back and forth, and you can bump the font size up and down or scroll the page if you need to. Hit Escape when you're done.&lt;/p&gt;
&lt;p&gt;Crucially, Present saves your URLs automatically any time you make a change. If the app crashes you can start it back up again and restore your presentation state.&lt;/p&gt;
&lt;p&gt;You can also save presentations as a &lt;code&gt;.txt&lt;/code&gt; file (literally a newline-delimited sequence of URLs) and load them back up again later.&lt;/p&gt;
&lt;h4 id="remote-controlled-via-my-phone"&gt;Remote controlled via my phone&lt;/h4&gt;
&lt;p&gt;Getting the initial app working took so little time that I decided to get more ambitious.&lt;/p&gt;
&lt;p&gt;It's neat having a remote control for a presentation...&lt;/p&gt;
&lt;p&gt;So I prompted:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Add a web server which listens on 0.0.0.0:9123 - the web server serves a single mobile-friendly page with prominent left and right buttons - clicking those buttons switches the slide left and right - there is also a button to start presentation mode or stop depending on the mode it is in.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I have &lt;a href="https://tailscale.com/"&gt;Tailscale&lt;/a&gt; on my laptop and my phone, which means I don't have to worry about Wi-Fi networks blocking access between the two devices. My phone can access &lt;code&gt;http://100.122.231.116:9123/&lt;/code&gt; directly from anywhere in the world and control the presentation running on my laptop.&lt;/p&gt;
&lt;p&gt;It took a few more iterative prompts to get to the final interface, which looked like this:&lt;/p&gt;
&lt;p style="text-align: center;"&gt;&lt;img src="https://static.simonwillison.net/static/2026/present-mobile.jpg" alt="Mobile phone web browser app with large buttons, Slide 4/31 at the top, Prev, Next and Start buttons, a thin bar with a up/down scroll icon and text size + and - buttons and the current slide URL at the bottom." style="max-width: 80%;" /&gt;&lt;/p&gt;
&lt;p&gt;There's a slide indicator at the top, prev and next buttons, a nice big "Start" button and buttons for adjusting the font size.&lt;/p&gt;
&lt;p&gt;The most complex feature is that thin bar next to the start button. That's a touch-enabled scroll bar - you can slide your finger up and down on it to scroll the currently visible web page up and down on the screen.&lt;/p&gt;
&lt;p&gt;It's &lt;em&gt;very&lt;/em&gt; clunky but it works just well enough to solve the problem of a page loading with most interesting content below the fold.&lt;/p&gt;
&lt;h4 id="learning-from-the-code"&gt;Learning from the code&lt;/h4&gt;
&lt;p&gt;I'd already &lt;a href="https://github.com/simonw/present"&gt;pushed the code to GitHub&lt;/a&gt; (with a big "This app was vibe coded [...] I make no promises other than it worked on my machine!" disclaimer) when I realized I should probably take a look at the code.&lt;/p&gt;
&lt;p&gt;I used this as an opportunity to document a recent pattern I've been using: asking the model to present a linear walkthrough of the entire codebase. Here's the resulting &lt;a href="https://simonwillison.net/guides/agentic-engineering-patterns/linear-walkthroughs/"&gt;Linear walkthroughs&lt;/a&gt; pattern in my ongoing &lt;a href="https://simonwillison.net/2026/Feb/23/agentic-engineering-patterns/"&gt;Agentic Engineering Patterns guide&lt;/a&gt;, including the prompt I used.&lt;/p&gt;
&lt;p&gt;The &lt;a href="https://github.com/simonw/present/blob/main/walkthrough.md"&gt;resulting walkthrough document&lt;/a&gt; is genuinely useful. It turns out Claude Code decided to implement the web server for the remote control feature &lt;a href="https://github.com/simonw/present/blob/main/walkthrough.md#request-routing"&gt;using socket programming without a library&lt;/a&gt;! Here's the minimal HTTP parser it used for routing:&lt;/p&gt;
&lt;div class="highlight highlight-source-swift"&gt;&lt;pre&gt;    &lt;span class="pl-k"&gt;private&lt;/span&gt; &lt;span class="pl-en"&gt;func&lt;/span&gt; route&lt;span class="pl-kos"&gt;(&lt;/span&gt;_ raw&lt;span class="pl-kos"&gt;:&lt;/span&gt; &lt;span class="pl-smi"&gt;String&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt; &lt;span class="pl-c1"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="pl-smi"&gt;String&lt;/span&gt; &lt;span class="pl-kos"&gt;{&lt;/span&gt;
        &lt;span class="pl-k"&gt;let&lt;/span&gt; &lt;span class="pl-s1"&gt;firstLine&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; raw&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;components&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;separatedBy&lt;span class="pl-kos"&gt;:&lt;/span&gt; &lt;span class="pl-s"&gt;"&lt;/span&gt;&lt;span class="pl-s"&gt;\r&lt;/span&gt;&lt;span class="pl-s"&gt;\n&lt;/span&gt;&lt;span class="pl-s"&gt;"&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;first &lt;span class="pl-c1"&gt;??&lt;/span&gt; &lt;span class="pl-s"&gt;"&lt;/span&gt;&lt;span class="pl-s"&gt;"&lt;/span&gt;
        &lt;span class="pl-k"&gt;let&lt;/span&gt; &lt;span class="pl-s1"&gt;parts&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; firstLine&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;split&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;separator&lt;span class="pl-kos"&gt;:&lt;/span&gt; &lt;span class="pl-s"&gt;"&lt;/span&gt;&lt;span class="pl-s"&gt; &lt;/span&gt;&lt;span class="pl-s"&gt;"&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;
        &lt;span class="pl-k"&gt;let&lt;/span&gt; &lt;span class="pl-s1"&gt;path&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; parts&lt;span class="pl-kos"&gt;.&lt;/span&gt;count &lt;span class="pl-c1"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="pl-c1"&gt;2&lt;/span&gt; &lt;span class="pl-c1"&gt;?&lt;/span&gt; &lt;span class="pl-en"&gt;String&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-en"&gt;parts&lt;/span&gt;&lt;span class="pl-kos"&gt;[&lt;/span&gt;&lt;span class="pl-c1"&gt;1&lt;/span&gt;&lt;span class="pl-kos"&gt;]&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt; &lt;span class="pl-k"&gt;:&lt;/span&gt; &lt;span class="pl-s"&gt;"&lt;/span&gt;&lt;span class="pl-s"&gt;/&lt;/span&gt;&lt;span class="pl-s"&gt;"&lt;/span&gt;

        &lt;span class="pl-k"&gt;switch&lt;/span&gt; path &lt;span class="pl-kos"&gt;{&lt;/span&gt;
        &lt;span class="pl-k"&gt;case&lt;/span&gt; &lt;span class="pl-s"&gt;"&lt;/span&gt;&lt;span class="pl-s"&gt;/next&lt;/span&gt;&lt;span class="pl-s"&gt;"&lt;/span&gt;&lt;span class="pl-kos"&gt;:&lt;/span&gt;
            state&lt;span class="pl-c1"&gt;&lt;span class="pl-c1"&gt;?&lt;/span&gt;&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;goToNext&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;
            &lt;span class="pl-k"&gt;return&lt;/span&gt; &lt;span class="pl-en"&gt;jsonResponse&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s"&gt;"&lt;/span&gt;&lt;span class="pl-s"&gt;ok&lt;/span&gt;&lt;span class="pl-s"&gt;"&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;
        &lt;span class="pl-k"&gt;case&lt;/span&gt; &lt;span class="pl-s"&gt;"&lt;/span&gt;&lt;span class="pl-s"&gt;/prev&lt;/span&gt;&lt;span class="pl-s"&gt;"&lt;/span&gt;&lt;span class="pl-kos"&gt;:&lt;/span&gt;
            state&lt;span class="pl-c1"&gt;&lt;span class="pl-c1"&gt;?&lt;/span&gt;&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;goToPrevious&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;
            &lt;span class="pl-k"&gt;return&lt;/span&gt; &lt;span class="pl-en"&gt;jsonResponse&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s"&gt;"&lt;/span&gt;&lt;span class="pl-s"&gt;ok&lt;/span&gt;&lt;span class="pl-s"&gt;"&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;
&lt;span class="pl-kos"&gt;&lt;/span&gt;&lt;span class="pl-kos"&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Using GET requests for state changes like that opens up some fun CSRF vulnerabilities. For this particular application I don't really care.&lt;/p&gt;
&lt;h4 id="expanding-our-horizons"&gt;Expanding our horizons&lt;/h4&gt;
&lt;p&gt;Vibe coding stories like this are ten a penny these days. I think this one is worth sharing for a few reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Swift, a language I don't know, was absolutely the right choice here. I wanted a full screen app that embedded web content and could be controlled over the network. Swift had everything I needed.&lt;/li&gt;
&lt;li&gt;When I finally did look at the code it was simple, straightforward and did exactly what I needed and not an inch more.&lt;/li&gt;
&lt;li&gt;This solved a real problem for me. I've always wanted a good way to serve a presentation as a sequence of pages, and now I have exactly that.&lt;/li&gt;
&lt;li&gt;I didn't have to open Xcode even once!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This doesn't mean native Mac developers are obsolete. I still used a whole bunch of my own accumulated technical knowledge (and the fact that I'd already installed Xcode and the like) to get this result, and someone who knew what they were doing could have built a far better solution in the same amount of time.&lt;/p&gt;
&lt;p&gt;It's a neat illustration of how those of us with software engineering experience can expand our horizons in fun and interesting directions. I'm no longer afraid of Swift! Next time I need a small, personal macOS app I know that it's achievable with our existing set of tools.&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/macos"&gt;macos&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai"&gt;ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/generative-ai"&gt;generative-ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/llms"&gt;llms&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai-assisted-programming"&gt;ai-assisted-programming&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/vibe-coding"&gt;vibe-coding&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/swift"&gt;swift&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/agentic-engineering"&gt;agentic-engineering&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/november-2025-inflection"&gt;november-2025-inflection&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="macos"/><category term="ai"/><category term="generative-ai"/><category term="llms"/><category term="ai-assisted-programming"/><category term="vibe-coding"/><category term="swift"/><category term="agentic-engineering"/><category term="november-2025-inflection"/></entry><entry><title>Linear walkthroughs</title><link href="https://simonwillison.net/guides/agentic-engineering-patterns/linear-walkthroughs/" rel="alternate"/><published>2026-02-25T01:07:10+00:00</published><updated>2026-02-25T01:07:10+00:00</updated><id>https://simonwillison.net/guides/agentic-engineering-patterns/linear-walkthroughs/</id><summary type="html">
    &lt;p&gt;&lt;em&gt;&lt;a href="https://simonwillison.net/guides/agentic-engineering-patterns/"&gt;Agentic Engineering Patterns&lt;/a&gt; &amp;gt;&lt;/em&gt;&lt;/p&gt;
    &lt;p&gt;Sometimes it's useful to have a coding agent give you a structured walkthrough of a codebase. &lt;/p&gt;
&lt;p&gt;Maybe it's existing code you need to get up to speed on, maybe it's your own code that you've forgotten the details of, or maybe you vibe coded the whole thing and need to understand how it actually works.&lt;/p&gt;
&lt;p&gt;Frontier models with the right agent harness can construct a detailed walkthrough to help you understand how code works.&lt;/p&gt;
&lt;h2 id="an-example-using-showboat-and-present"&gt;An example using Showboat and Present&lt;/h2&gt;
&lt;p&gt;I recently &lt;a href="https://simonwillison.net/2026/Feb/25/present/"&gt;vibe coded a SwiftUI slide presentation app&lt;/a&gt; on my Mac using Claude Code and Opus 4.6.&lt;/p&gt;
&lt;p&gt;I was speaking about the advances in frontier models between November 2025 and February 2026, and I like to include at least one gimmick in my talks (a &lt;a href="https://simonwillison.net/2019/Dec/10/better-presentations/"&gt;STAR moment&lt;/a&gt; - Something They'll Always Remember). In this case I decided the gimmick would be revealing at the end of the presentation that the slide mechanism itself was an example of what vibe coding could do.&lt;/p&gt;
&lt;p&gt;I released the code &lt;a href="https://github.com/simonw/present"&gt;to GitHub&lt;/a&gt; and then realized I didn't know anything about how it actually worked - I had prompted the whole thing into existence (&lt;a href="https://gisthost.github.io/?bfbc338977ceb71e298e4d4d5ac7d63c"&gt;partial transcript here&lt;/a&gt;) without paying any attention to the code it was writing.&lt;/p&gt;
&lt;p&gt;So I fired up a new instance of Claude Code for web, pointed it at my repo and prompted:
&lt;pre&gt;Read the source and then plan a linear walkthrough of the code that explains how it all works in detail

Then run “uvx showboat –help” to learn showboat - use showboat to create a walkthrough.md file in the repo and build the walkthrough in there, using showboat note for commentary and showboat exec plus sed or grep or cat or whatever you need to include snippets of code you are talking about&lt;/pre&gt;
&lt;a href="https://github.com/simonw/showboat"&gt;Showboat&lt;/a&gt; is a tool I built to help coding agents write documents that demonstrate their work. You can see the &lt;a href="https://github.com/simonw/showboat/blob/main/help.txt"&gt;showboat --help output here&lt;/a&gt;, which is designed to give the model everything it needs to know in order to use the tool.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;showboat note&lt;/code&gt; command adds Markdown to the document. The &lt;code&gt;showboat exec&lt;/code&gt; command accepts a shell command, executes it and then adds both the command and its output to the document.&lt;/p&gt;
&lt;p&gt;By telling it to use "sed or grep or cat or whatever you need to include snippets of code you are talking about" I ensured that Claude Code would not manually copy snippets of code into the document, since that could introduce a risk of hallucinations or mistakes.&lt;/p&gt;
&lt;p&gt;This worked extremely well. Here's the &lt;a href="https://github.com/simonw/present/blob/main/walkthrough.md"&gt;document Claude Code created with Showboat&lt;/a&gt;, which talks through all six &lt;code&gt;.swift&lt;/code&gt; files in detail and provides a clear and actionable explanation about how the code works.&lt;/p&gt;
&lt;p&gt;I learned a great deal about how SwiftUI apps are structured and absorbed some solid details about the Swift language itself just from reading this document.&lt;/p&gt;
&lt;p&gt;If you are concerned that LLMs might reduce the speed at which you learn new skills I strongly recommend adopting patterns like this one.  Even a ~40 minute vibe coded toy project can become an opportunity to explore new ecosystems and pick up some interesting new tricks.&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/agentic-engineering"&gt;agentic-engineering&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai"&gt;ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/llms"&gt;llms&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/vibe-coding"&gt;vibe-coding&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/coding-agents"&gt;coding-agents&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai-assisted-programming"&gt;ai-assisted-programming&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/swift"&gt;swift&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/generative-ai"&gt;generative-ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/showboat"&gt;showboat&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="agentic-engineering"/><category term="ai"/><category term="llms"/><category term="vibe-coding"/><category term="coding-agents"/><category term="ai-assisted-programming"/><category term="swift"/><category term="generative-ai"/><category term="showboat"/></entry><entry><title>Writing about Agentic Engineering Patterns</title><link href="https://simonwillison.net/2026/Feb/23/agentic-engineering-patterns/" rel="alternate"/><published>2026-02-23T17:43:02+00:00</published><updated>2026-02-23T17:43:02+00:00</updated><id>https://simonwillison.net/2026/Feb/23/agentic-engineering-patterns/</id><summary type="html">
    &lt;p&gt;I've started a new project to collect and document &lt;strong&gt;&lt;a href="https://simonwillison.net/guides/agentic-engineering-patterns/"&gt;Agentic Engineering Patterns&lt;/a&gt;&lt;/strong&gt; - coding practices and patterns to help get the best results out of this new era of coding agent development we find ourselves entering.&lt;/p&gt;
&lt;p&gt;I'm using &lt;strong&gt;Agentic Engineering&lt;/strong&gt; to refer to building software using coding agents - tools like Claude Code and OpenAI Codex, where the defining feature is that they can both generate and &lt;em&gt;execute&lt;/em&gt; code - allowing them to test that code and iterate on it independently of turn-by-turn guidance from their human supervisor.&lt;/p&gt;
&lt;p&gt;I think of &lt;strong&gt;vibe coding&lt;/strong&gt; using its &lt;a href="https://simonwillison.net/2025/Mar/19/vibe-coding/"&gt;original definition&lt;/a&gt; of coding where you pay no attention to the code at all, which today is often associated with non-programmers using LLMs to write code.&lt;/p&gt;
&lt;p&gt;Agentic Engineering represents the other end of the scale: professional software engineers using coding agents to improve and accelerate their work by amplifying their existing expertise.&lt;/p&gt;
&lt;p&gt;There is so much to learn and explore about this new discipline! I've already published a lot &lt;a href="https://simonwillison.net/tags/ai-assisted-programming/"&gt;under my ai-assisted-programming tag&lt;/a&gt; (345 posts and counting) but that's been relatively unstructured. My new goal is to produce something that helps answer the question "how do I get good results out of this stuff" all in one place.&lt;/p&gt;
&lt;p&gt;I'll be developing and growing this project here on my blog as a series of chapter-shaped patterns, loosely inspired by the format popularized by &lt;a href="https://en.wikipedia.org/wiki/Design_Patterns"&gt;Design Patterns: Elements of Reusable Object-Oriented Software&lt;/a&gt; back in 1994.&lt;/p&gt;
&lt;p&gt;I published the first two chapters today:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://simonwillison.net/guides/agentic-engineering-patterns/code-is-cheap/"&gt;Writing code is cheap now&lt;/a&gt;&lt;/strong&gt; talks about the central challenge of agentic engineering: the cost to churn out initial working code has dropped to almost nothing, how does that impact our existing intuitions about how we work, both individually and as a team?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://simonwillison.net/guides/agentic-engineering-patterns/red-green-tdd/"&gt;Red/green TDD&lt;/a&gt;&lt;/strong&gt; describes how test-first development helps agents write more succinct and reliable code with minimal extra prompting.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I hope to add more chapters at a rate of 1-2 a week. I don't really know when I'll stop, there's a lot to cover!&lt;/p&gt;
&lt;h4 id="written-by-me-not-by-an-llm"&gt;Written by me, not by an LLM&lt;/h4&gt;
&lt;p&gt;I have a strong personal policy of not publishing AI-generated writing under my own name. That policy will hold true for Agentic Engineering Patterns as well. I'll be using LLMs for proofreading and fleshing out example code and all manner of other side-tasks, but the words you read here will be my own.&lt;/p&gt;
&lt;h4 id="chapters-and-guides"&gt;Chapters and Guides&lt;/h4&gt;
&lt;p&gt;Agentic Engineering Patterns isn't exactly &lt;em&gt;a book&lt;/em&gt;, but it's kind of book-shaped. I'll be publishing it on my site using a new shape of content I'm calling a &lt;em&gt;guide&lt;/em&gt;. A guide is a collection of chapters, where each chapter is effectively a blog post with a less prominent date that's designed to be updated over time, not frozen at the point of first publication.&lt;/p&gt;
&lt;p&gt;Guides and chapters are my answer to the challenge of publishing "evergreen" content on a blog. I've been trying to find a way to do this for a while now. This feels like a format that might stick.&lt;/p&gt;

&lt;p&gt;If you're interested in the implementation you can find the code in the &lt;a href="https://github.com/simonw/simonwillisonblog/blob/b9cd41a0ac4a232b2a6c90ca3fff9ae465263b02/blog/models.py#L262-L280"&gt;Guide&lt;/a&gt;, &lt;a href="https://github.com/simonw/simonwillisonblog/blob/b9cd41a0ac4a232b2a6c90ca3fff9ae465263b02/blog/models.py#L349-L405"&gt;Chapter&lt;/a&gt; and &lt;a href="https://github.com/simonw/simonwillisonblog/blob/b9cd41a0ac4a232b2a6c90ca3fff9ae465263b02/blog/models.py#L408-L423"&gt;ChapterChange&lt;/a&gt; models and the &lt;a href="https://github.com/simonw/simonwillisonblog/blob/b9cd41a0ac4a232b2a6c90ca3fff9ae465263b02/blog/views.py#L775-L923"&gt;associated Django views&lt;/a&gt;, almost all of which was written by Claude Opus 4.6 running in Claude Code for web accessed via my iPhone.&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/blogging"&gt;blogging&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/design-patterns"&gt;design-patterns&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/projects"&gt;projects&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/writing"&gt;writing&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai"&gt;ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/generative-ai"&gt;generative-ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/llms"&gt;llms&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai-assisted-programming"&gt;ai-assisted-programming&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/vibe-coding"&gt;vibe-coding&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/coding-agents"&gt;coding-agents&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/agentic-engineering"&gt;agentic-engineering&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/site-upgrades"&gt;site-upgrades&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="blogging"/><category term="design-patterns"/><category term="projects"/><category term="writing"/><category term="ai"/><category term="generative-ai"/><category term="llms"/><category term="ai-assisted-programming"/><category term="vibe-coding"/><category term="coding-agents"/><category term="agentic-engineering"/><category term="site-upgrades"/></entry><entry><title>Quoting Paul Ford</title><link href="https://simonwillison.net/2026/Feb/23/paul-ford/" rel="alternate"/><published>2026-02-23T16:00:32+00:00</published><updated>2026-02-23T16:00:32+00:00</updated><id>https://simonwillison.net/2026/Feb/23/paul-ford/</id><summary type="html">
    &lt;blockquote cite="https://ftrain.com/leading-thoughts"&gt;&lt;p&gt;The paper asked me to explain vibe coding, and I did so, because I think something big is coming there, and I'm deep in, and I worry that normal people are not able to see it and I want them to be prepared. But people can't just read something and hate you quietly; they can't see that you have provided them with a utility or a warning; they need their screech. You are distributed to millions of people, and become the local proxy for the emotions of maybe dozens of people, who disagree and demand your attention, and because you are the one in the paper you need to welcome them with a pastor's smile and deep empathy, and if you speak a word in your own defense they'll screech even louder.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p class="cite"&gt;&amp;mdash; &lt;a href="https://ftrain.com/leading-thoughts"&gt;Paul Ford&lt;/a&gt;, on writing about vibe coding for the New York Times&lt;/p&gt;

    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/vibe-coding"&gt;vibe-coding&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/new-york-times"&gt;new-york-times&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/paul-ford"&gt;paul-ford&lt;/a&gt;&lt;/p&gt;



</summary><category term="vibe-coding"/><category term="new-york-times"/><category term="paul-ford"/></entry><entry><title>How Generative and Agentic AI Shift Concern from Technical Debt to Cognitive Debt</title><link href="https://simonwillison.net/2026/Feb/15/cognitive-debt/" rel="alternate"/><published>2026-02-15T05:20:11+00:00</published><updated>2026-02-15T05:20:11+00:00</updated><id>https://simonwillison.net/2026/Feb/15/cognitive-debt/</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://margaretstorey.com/blog/2026/02/09/cognitive-debt/"&gt;How Generative and Agentic AI Shift Concern from Technical Debt to Cognitive Debt&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
This piece by Margaret-Anne Storey is the best explanation of the term &lt;strong&gt;cognitive debt&lt;/strong&gt; I've seen so far.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Cognitive debt&lt;/em&gt;, a term gaining &lt;a href="https://www.media.mit.edu/publications/your-brain-on-chatgpt/"&gt;traction&lt;/a&gt; recently, instead communicates the notion that the debt compounded from going fast lives in the brains of the developers and affects their lived experiences and abilities to “go fast” or to make changes. Even if AI agents produce code that could be easy to understand, the humans involved may have simply lost the plot and may not understand what the program is supposed to do, how their intentions were implemented, or how to possibly change it.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Margaret-Anne expands on this further with an anecdote about a student team she coached:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;But by weeks 7 or 8, one team hit a wall. They could no longer make even simple changes without breaking something unexpected. When I met with them, the team initially blamed technical debt: messy code, poor architecture, hurried implementations. But as we dug deeper, the real problem emerged: no one on the team could explain why certain design decisions had been made or how different parts of the system were supposed to work together. The code might have been messy, but the bigger issue was that the theory of the system, their shared understanding, had fragmented or disappeared entirely. They had accumulated cognitive debt faster than technical debt, and it paralyzed them.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I've experienced this myself on some of my more ambitious vibe-code-adjacent projects. I've been experimenting with prompting entire new features into existence without reviewing their implementations and, while it works surprisingly well, I've found myself getting lost in my own projects.&lt;/p&gt;
&lt;p&gt;I no longer have a firm mental model of what they can do and how they work, which means each additional feature becomes harder to reason about, eventually leading me to lose the ability to make confident decisions about where to go next.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://martinfowler.com/fragments/2026-02-13.html"&gt;Martin Fowler&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/definitions"&gt;definitions&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai"&gt;ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/generative-ai"&gt;generative-ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/llms"&gt;llms&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai-assisted-programming"&gt;ai-assisted-programming&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/vibe-coding"&gt;vibe-coding&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/cognitive-debt"&gt;cognitive-debt&lt;/a&gt;&lt;/p&gt;



</summary><category term="definitions"/><category term="ai"/><category term="generative-ai"/><category term="llms"/><category term="ai-assisted-programming"/><category term="vibe-coding"/><category term="cognitive-debt"/></entry><entry><title>GLM-5: From Vibe Coding to Agentic Engineering</title><link href="https://simonwillison.net/2026/Feb/11/glm-5/" rel="alternate"/><published>2026-02-11T18:56:14+00:00</published><updated>2026-02-11T18:56:14+00:00</updated><id>https://simonwillison.net/2026/Feb/11/glm-5/</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://z.ai/blog/glm-5"&gt;GLM-5: From Vibe Coding to Agentic Engineering&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
This is a &lt;em&gt;huge&lt;/em&gt; new MIT-licensed model: 744B parameters and &lt;a href="https://huggingface.co/zai-org/GLM-5"&gt;1.51TB on Hugging Face&lt;/a&gt; twice the size of &lt;a href="https://huggingface.co/zai-org/GLM-4.7"&gt;GLM-4.7&lt;/a&gt; which was 368B and 717GB (4.5 and 4.6 were around that size too).&lt;/p&gt;
&lt;p&gt;It's interesting to see Z.ai take a position on what we should call professional software engineers building with LLMs - I've seen &lt;strong&gt;Agentic Engineering&lt;/strong&gt; show up in a few other places recently. most notable &lt;a href="https://twitter.com/karpathy/status/2019137879310836075"&gt;from Andrej Karpathy&lt;/a&gt; and &lt;a href="https://addyosmani.com/blog/agentic-engineering/"&gt;Addy Osmani&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I ran my "Generate an SVG of a pelican riding a bicycle" prompt through GLM-5 via &lt;a href="https://openrouter.ai/"&gt;OpenRouter&lt;/a&gt; and got back &lt;a href="https://gist.github.com/simonw/cc4ca7815ae82562e89a9fdd99f0725d"&gt;a very good pelican on a disappointing bicycle frame&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img alt="The pelican is good and has a well defined beak. The bicycle frame is a wonky red triangle. Nice sun and motion lines." src="https://static.simonwillison.net/static/2026/glm-5-pelican.png" /&gt;

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://news.ycombinator.com/item?id=46977210"&gt;Hacker News&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/definitions"&gt;definitions&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai"&gt;ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/generative-ai"&gt;generative-ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/llms"&gt;llms&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai-assisted-programming"&gt;ai-assisted-programming&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/pelican-riding-a-bicycle"&gt;pelican-riding-a-bicycle&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/llm-release"&gt;llm-release&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/vibe-coding"&gt;vibe-coding&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/openrouter"&gt;openrouter&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai-in-china"&gt;ai-in-china&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/glm"&gt;glm&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/agentic-engineering"&gt;agentic-engineering&lt;/a&gt;&lt;/p&gt;



</summary><category term="definitions"/><category term="ai"/><category term="generative-ai"/><category term="llms"/><category term="ai-assisted-programming"/><category term="pelican-riding-a-bicycle"/><category term="llm-release"/><category term="vibe-coding"/><category term="openrouter"/><category term="ai-in-china"/><category term="glm"/><category term="agentic-engineering"/></entry><entry><title>sqlite-ast 0.1a0</title><link href="https://simonwillison.net/2026/Jan/30/sqlite-ast-2/" rel="alternate"/><published>2026-01-30T06:12:45+00:00</published><updated>2026-01-30T06:12:45+00:00</updated><id>https://simonwillison.net/2026/Jan/30/sqlite-ast-2/</id><summary type="html">
    
        &lt;p&gt;&lt;strong&gt;Release:&lt;/strong&gt; &lt;a href="https://github.com/simonw/sqlite-ast/releases/tag/0.1a0"&gt;sqlite-ast 0.1a0&lt;/a&gt;&lt;/p&gt;
        &lt;p&gt;I wanted a Python library that could parse SQLite SELECT statements, so I vibe coded this one up based on a specification I reverse-engineered from SQLite's own parser behavior.&lt;/p&gt;
&lt;p&gt;There's an &lt;a href="https://tools.simonwillison.net/sqlite-ast"&gt;interactive playground here&lt;/a&gt; for trying it out in the browser (via Pyodide).&lt;/p&gt;
    
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/sqlite"&gt;sqlite&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/vibe-coding"&gt;vibe-coding&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="python"/><category term="sqlite"/><category term="vibe-coding"/></entry><entry><title>Don't "Trust the Process"</title><link href="https://simonwillison.net/2026/Jan/24/dont-trust-the-process/" rel="alternate"/><published>2026-01-24T23:31:03+00:00</published><updated>2026-01-24T23:31:03+00:00</updated><id>https://simonwillison.net/2026/Jan/24/dont-trust-the-process/</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.youtube.com/watch?v=4u94juYwLLM"&gt;Don&amp;#x27;t &amp;quot;Trust the Process&amp;quot;&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Jenny Wen, Design Lead at Anthropic (and previously Director of Design at Figma) gave a provocative keynote at Hatch Conference in Berlin last September.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Don't &amp;quot;Trust the process&amp;quot; slide, speaker shown on the left" src="https://static.simonwillison.net/static/2026/dont-trust-process.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;Jenny argues that the Design Process - user research leading to personas leading to user journeys leading to wireframes... all before anything gets built - may be outdated for today's world.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Hypothesis&lt;/strong&gt;: In a world where anyone can make anything — what matters is your ability to choose and curate what you make.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In place of the Process, designers should lean into prototypes. AI makes these much more accessible and less time-consuming than they used to be.&lt;/p&gt;
&lt;p&gt;Watching this talk made me think about how AI-assisted programming significantly reduces the cost of building the &lt;em&gt;wrong&lt;/em&gt; thing. Previously if the design wasn't right you could waste months of development time building in the wrong direction, which was a very expensive mistake. If a wrong direction wastes just a few days instead we can take more risks and be much more proactive in exploring the problem space.&lt;/p&gt;
&lt;p&gt;I've always been a compulsive prototyper though, so this is very much playing into my own existing biases!

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://twitter.com/jenny_wen/status/2014479445738893649"&gt;@jenny_wen&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/design"&gt;design&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/prototyping"&gt;prototyping&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai"&gt;ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/generative-ai"&gt;generative-ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/llms"&gt;llms&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai-assisted-programming"&gt;ai-assisted-programming&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/vibe-coding"&gt;vibe-coding&lt;/a&gt;&lt;/p&gt;



</summary><category term="design"/><category term="prototyping"/><category term="ai"/><category term="generative-ai"/><category term="llms"/><category term="ai-assisted-programming"/><category term="vibe-coding"/></entry><entry><title>Quoting Jasmine Sun</title><link href="https://simonwillison.net/2026/Jan/24/jasmine-sun/" rel="alternate"/><published>2026-01-24T21:34:35+00:00</published><updated>2026-01-24T21:34:35+00:00</updated><id>https://simonwillison.net/2026/Jan/24/jasmine-sun/</id><summary type="html">
    &lt;blockquote cite="https://jasmi.news/p/claude-code"&gt;&lt;p&gt;&lt;strong&gt;If you tell a friend they can now instantly create any app, they’ll probably say “Cool! Now I need to think of an idea.”&lt;/strong&gt; Then they will forget about it, and never build a thing. The problem is not that your friend is horribly uncreative. It’s that most people’s problems are not software-shaped, and most won’t notice even when they are. [...]&lt;/p&gt;
&lt;p&gt;Programmers are trained to see everything as a software-shaped problem: if you do a task three times, you should probably automate it with a script. &lt;em&gt;Rename every IMG_*.jpg file from the last week to hawaii2025_*.jpg&lt;/em&gt;, they tell their terminal, while the rest of us painfully click and copy-paste. We are blind to the solutions we were never taught to see, asking for faster horses and never dreaming of cars.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p class="cite"&gt;&amp;mdash; &lt;a href="https://jasmi.news/p/claude-code"&gt;Jasmine Sun&lt;/a&gt;&lt;/p&gt;

    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/vibe-coding"&gt;vibe-coding&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/coding-agents"&gt;coding-agents&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/claude-code"&gt;claude-code&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/generative-ai"&gt;generative-ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai"&gt;ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/llms"&gt;llms&lt;/a&gt;&lt;/p&gt;



</summary><category term="vibe-coding"/><category term="coding-agents"/><category term="claude-code"/><category term="generative-ai"/><category term="ai"/><category term="llms"/></entry></feed>