Simon Willison’s Weblog

Newsletter
Atom feed

Elsewhere

Filters: Sorted by date

I realized I didn't know where Yonkers was, so I looked it up on Google Maps and then, to help embed its location and shape in my head (and to try out Claude Fable 5.2's 3D modeling and animation skills), I prompted Fable 5.2 Medium to:

Make a three js animation where yonkers stands up as a big mech (cool stand up motion) and átomos around manhattan and queens and Brooklyn, use openstreetmap mapping data

It correctly deduced that "átomos" was a mobile keyboard typo for "stomps":

Anthropic released Fable 5.2 and Mythos 5.2 today. Fable 5.2 is currently ranked first on Artificial Analysis. Anthropic say it "solved two open problems in the physics of black holes", so clearly their internal benchmarks have become more ambitious!

I've added the new models to llm-anthropic, so you can now run prompts through Fable 5.2 like this:

uv tool install llm
llm install llm-anthropic
llm keys set anthropic
# paste key here
llm -m claude-fable-5.2 "Generate an SVG of a pelican riding a bicycle"

I ran pelicans at all five reasoning levels - low, medium, high, xhigh, and max. You can see them all here - here's the best result I got, from xhigh:

It's good - legs the right side of the bicycle, good frame, nice pouch, weird little fingers on the handlebars. The pouch looks slightly misaligned with the top of the beak.

max failed to return an image at all, running out of tokens after spending 128,000 thinking about them. That's the same bug that I saw with Opus 5.5 - but it's new for the Fable series, both Fable 5 and Fable 5.1 returned max effort pelicans just fine.

It's priced at $8/million input and $40/million output, which is a 20% discount on the previous Fable 5.1 ($10/$50).

I also used Fable 5.2 to help identify the source of a mysterious beep.

Comment My comment on The Web Is More Than Enough, by Paul Ford — Hacker News

OK, this comment convinced me to give that a shot.

Here's a thing I made in Three.js this morning - a Nudibranch themed Faberge Egg: https://static.simonwillison.net/static/2026/Hidden-Reef-Egg... (77KB)

I just asked Claude (Opus 5.5 Medium running in regular Claude Chat) to port that to Vanilla JS, in the same thread where it had built it, and got this: https://static.simonwillison.net/static/2026/Hidden-Reef-Egg... (98KB)

They aren't identical - you can see what Claude said about that in the transcript (I don't fully understand why it differed): https://claude.ai/share/e56d163e-2793-4677-aa11-191d5058b414

But yeah, it turns out Claude Opus 5.5 is perfectly capable of 3D modeling in Vanilla JS + WebGL without Three.js.

In eyeballing the code I find the Three.js slightly easier to understand myself, but I rarely look at the code for these 3D projects so that isn't a particularly big deal.

# 9th November 2026, 4:32 am / claude, javascript, three-js, hacker-news, generative-ai, ai, llms

I was out walking the dog when I got curious about how easy/difficult it would be to write a SQLite C extension that adds a parse_number() function for turning strings like -$1,234.56 or '12.5%' into numeric values that could be summed and sorted.

I fired up regular Claude chat (Opus 5.5) and prompted:

I'm thinking about a SQLite custom function 'parse_number(value)' that strips ',', '$', '£' and '%', handles a leading '-', and returns 'NULL' for anything it can’t parse

Explore the difference between building this as a SQLite extension in C v.s. in Rust - I want to compare performance and code complexity and size of the resulting.so(or whatever) file

This gave me a solid starting point - the Rust extension was 321 KB (using Rusqlite) and 298 KB (using sqlite-loadable-rs), while the C extension was 14 KB. But the Rust extension was faster!

Turns out that's because the Rust equivalent of C's strtod is "twice as fast". So I prompted:

Try writing your own C function for parsing text into a decimal number that catches up to or beats the Rust implementation

And it did, using "Clinger's fast path":

Net cost per row over 2 million rows, lower is better:

first C new C rusqlite sqlite-loadable
Mixed data 67 ns 19 ns 46 ns 41 ns
Integers only 25 ns 16 ns 36 ns 32 ns
Decimals only 106 ns 18 ns 55 ns 47 ns

Finally, I had it rewrite the prototype as a research report to match the others in simonw/research by prompting this:

Clone simonw/research from GitHub and turn your above investigation into a research report as seen in that repository - you can give me a zip file to download that contains your new folder

Here's the resulting report.

Sighting 11:05 AM – 11:40 AM — Great Blue Heron, Western Grebe, Botta's Pocket Gopher, California Brown Pelican, California Sea Lion, Pacific Harbor Seal, in Monterey Bay National Marine Sanctuary, CA, US, CA
Great Blue Heron
Great Blue Heron
Great Blue Heron
Great Blue Heron
Western Grebe
Western Grebe
Botta's Pocket Gopher
Botta's Pocket Gopher
California Brown Pelican
California Brown Pelican
California Sea Lion
California Sea Lion
Pacific Harbor Seal
Pacific Harbor Seal

Comment My comment on Terry Pratchett's Study — Hacker News

Terry Pratchett once wrote about the concept of "horizontal wealth". Vertical wealth is where you spend money on rich people stuff - yachts and suchlike. Horizontal wealth is where you spend more money on the things you liked before you got rich: https://chibuikeani.blogspot.com/2017/02/the-price-of-happin...

But horizontal wealth means not letting your increased income dictate your tastes. You like books and now you have money? Buy more books! Change those catenary bookshelves for good hardwood ones! In my case, build a library extension to your office. And, of course, you buy what will be useful for that most wonderful of pursuits, blind research, which is research without direction for the sheer joy of it.

(Original source is his book A Slip of the Keyboard - this linked blog post quotes that bit.)

# 7th November 2026, 7:34 pm / hacker-news, terry-pratchett

Sighting 5:49 PM — Brandt's Cormorant, in Pillar Point Harbor, CA, US
Brandt's Cormorant
Brandt's Cormorant
Brandt's Cormorant
Brandt's Cormorant

Fixes a bug that affected my agent.datasette.io demo site, where the set of available LLM models was not correctly restricted to just those in the configuration.

Datasette Apps adds support for hosting and editing custom HTML applications inside of Datasette.

These can be created by hand, but it's easier to create them using LLMs via Datasette Agent. This release improves that integration:

  • Apps can now be tagged in Datasette Agent chat messages by typing #, which autocompletes from the apps the user has permission to view.
  • New app_add_stored_queries() and app_remove_stored_queries() tools let the agent manage which stored queries an app is allowed to call. Granting a write query requires approval from the user first.
  • app_create() now checks that each requested stored query exists and that the user can view it, and asks the user to approve any write queries before the app is created.
  • The "View app" buttons shown by the agent tools, and the links on # app references, now open the app full screen in the Datasette Agent panel.

This is a significant release of Datasette Agent. The most notable new feature is called the panel - it lets the agent chat open and then sit next to pages from Datasette, for example a table or query page.

When combined with datasette-apps this means you can prompt a database-backed app into existence and then modify it from the chat that sits next to that app.

Screenshot of a split-screen chat interface titled "evaluate llm prices" (model tag "gpt-6-luna"): the conversation runs in a narrow column on the left, and a wide side panel on the right displays the app the assistant built, with "EXPORT" and "Close panel" buttons above it. In the chat, blue user bubbles read "Build an app to help explore this data" and "give it a more hacker aesthetic design", each followed by collapsed tool calls (app_create, app_render, app_view, app_str_replace), "LLM Price Explorer v1." and "v2." entries with "View app" and "Edit" buttons, and short assistant replies describing the app and its new dark terminal-inspired theme. The right panel shows the resulting "LLM Price Explorer" in neon green monospace on black: stat cards for 185 models from 12 providers with workload costs from $0.042 (Jev 1.13) to $300.00 (o1 Pro), filter and workload cost estimate controls, and a table of matching models with input, output and cached input prices per million tokens.

You can try it by signing in with your GitHub account to agent.datasette.io.

A bugfix release for LLM, with contributions from Steven Huang, Tao An, Arjan Mossel, and XQ.

Comment My comment on AI is destroying my CS degree — Hacker News

I don't envy educators right now.

AI clearly means you need to change how you teach and evaluate students. We are only a few years in though, and it's still extremely unclear what effective teaching with these tools looks like.

The worst part is that the evaluation loop is measured in multiple years! If you start trying some new education mechanism today you won't get a robust idea of how well it works until your current cohort graduate in 3-6 years time.

Students effectively all become guinea pigs for untested new ways of learning and teaching.

# 3rd November 2026, 4:42 pm / ai-ethics, hacker-news, education, ai

There's a lot of stuff in this new release of my LLM CLI tool and Python library, representing around two months of accumulated features and fixes.

I added gpt-6.1-sol and gpt-6.1-terra, plus a new llm openai endpoint add command to make it easier to configure additional models that use OpenAI-compatible endpoints.

I switched the default model (if you have not set your own default) to gpt-6-luna - half the price of the previous default, gpt-5.6-luna.

LLM's support for embeddings now accepts that different binary content may have different content-type - necessary for models like Gemini Embedding 2, which can embed text, audio, images, and video.

A lot of open source maintainers complain about a flood of AI-assisted PRs. LLM is my project that has been most affected by this and, to be honest, I'm not finding it to be a problem. That's mainly because I have Opus 5.5 or GPT-6 Astra help me with triage to spot the most useful fixes!

This release includes fixes from 12 authors - almost all AI-assisted - and the burden of reviewing them wasn't nearly as great as I expected. I really see them as bug reports with attached examples - sometimes I land them, sometimes I implement my own fix.

So thanks to 0d00ciallo0721, ac1982, Allen Flux, Bian Yuhan, Bishop Kammeraad, Chen Yufeiyang, Christopher Krah, Ethan Stoner, Jiachen Xie, Olamide Faniyan, Sharat Chandra Sai Boddu, nosmile99 for helping out with (or spending tokens on) this release!

Sighting 3:18 PM – 3:23 PM — California Sea Lion, California Brown Pelican, in Pillar Point Harbor, CA, US
California Sea Lion
California Sea Lion
California Brown Pelican
California Brown Pelican
California Sea Lion
California Sea Lion
Sighting 12:33 PM — Black Phoebe, in San Mateo County, CA, US
Black Phoebe
Black Phoebe

I have some big upgrades to Datasette Agent in the pipeline, but in the meantime here's a tiny user experience improvement to the underlying datasette-llm plugin.

It now picks up the default model used by LLM, which means you can start using Datasette Agent without needing to explicitly configure a default model:

uvx --with datasette-agent datasette content.db --internal internal.db

Previously you would also need to specify a model with -s plugins.datasette-agent.default_model gpt-6-astra.

Sighting 10:39 AM – 10:50 AM — Brewer's Blackbird, California Brown Pelican, California Sea Lion, in Monterey Bay National Marine Sanctuary, CA, US, CA
Brewer's Blackbird
Brewer's Blackbird
California Brown Pelican
California Brown Pelican
California Brown Pelican
California Brown Pelican
California Sea Lion
California Sea Lion
Sighting 9:08 PM — California Brown Pelican, in San Francisco County, US, CA
California Brown Pelican
California Brown Pelican
Sighting 2:26 PM – 2:30 PM — Osprey, California Sea Lion, in Monterey Bay National Marine Sanctuary, CA, US, CA
Osprey
Osprey
California Sea Lion
California Sea Lion

webframe is a neat little project from Jeff Lindsay which lets you "run a web page like a program" - you point it at a URL or file and it executes it in a hidden headless web view and outputs console.log() to standard output and any exceptions to standard error.

It's written in Go on top of the glaze library, which provides access to a system browser ("WKWebView on macOS, WebKitGTK on Linux, WebView2 on Windows") so it doesn't need to bundle its own browser engine.

It reminds me of my own shot-scraper javascript tool, albeit in a much smaller package that doesn't depend on Playwright.

I had Claude Opus 5.5 put it through its paces and prepare a research report for me. Here's the prompt I used:

Explore https://github.com/tractordev/webframe try running it and put it though its paces, build a few exploratory prototypes, then evaluate it as a potential sandboxing tool and as a tool for scraping web pages using the same patterns encouraged by the "shot-scraper JavaScript" command (see simonw/shot-scraper on GitHub, docs directory)

I built this weird and very specific cropping tool - it lets you create a horizontal or vertical cut across an image, then slide the two sides relative to each other. Effectively it lets you crop out a section in the middle of an image.

I wanted this because I often use Tailor on my iPhone to stitch several screenshots together into lot long image, but sometimes I find myself wanting to crop out a section in the middle - to redact an irrelevant prompt from a transcript, for example.

I added four player online mode to Battledrones - previous maps were limited to two.

Here's the initial prompt I used:

Add a new map for a four player battle - four way rotational symmetry, up to four AI players, works with the WebSocket non-audio based multiplayer mode so up to four human players can join (any spare slots use AI)

Claude Opus 5.5 added a new map called "Compass Rose" and modified the WebSockets multiplayer network code to support up to four players for that map, joining via share links or QR codes.

The new map does indeed look a bit like a rose - it uses the cold and ice biome. Also shown are purple robots battling on the map.

The new teams use robots themed in purple and green, joining the existing blue and red designs. You can preview those colors in the updated model browser.

Here's a replay link to watch a four way AI battle with the "Goliath Armada" mutator turned on, where each side starts with four giant Goliath mech units.

Prompt to Claude Opus 5.5 High:

Build an artifact where you open a photo and it rapidly (in a few seconds) paints a version of that photo using a canvas element and simulated paintbrush

See also my previous experiment getting Claude Fable to simulate a watercolor illustration with P5.js.

A simulated oil painting of a photograph of a heron

Quick Study uses Sobel edge detection, with no external libraries beyond Google Fonts.

Sighting 6:51 PM – 7:01 PM — Pacific Harbor Seal, Great Blue Heron, California Brown Pelican, California Sea Lion, in Monterey Bay National Marine Sanctuary, CA, US, CA
Pacific Harbor Seal
Pacific Harbor Seal
Great Blue Heron
Great Blue Heron
Great Blue Heron
Great Blue Heron
California Brown Pelican
California Brown Pelican
California Sea Lion
California Sea Lion
California Sea Lion
California Sea Lion

When I switched LLM over to httpx2 in LLM 0.33 I forget that this would break any plugins which used httpx without directly depending on it. llm-tools-datasette was one of those plugins, now fixed.

I'm working towards a new LLM release, and testing PRs caused me to revisit the llm-echo debug plugin. This plugin provides a "model" which doesn't involve an LLM at all - it just repeats back the prompts that have been fed to it. The new release now updates the simulated token usage counts to take tool calls, system prompts, and reasoning tokens into account.

Example usage:

uvx --with llm-echo llm -m echo 'hi there' -o thinking 1 --usage

Outputs:

First I consider the prompt, then I decide what to say.
{
  "prompt": "hi there",
  "system": "",
  "attachments": [],
  "stream": true,
  "previous": []
}
Token usage: 2 input, 24 output
Sighting 2:07 PM – 2:12 PM — House Sparrow, Eastern Gray Squirrel, in Philadelphia Center City, PA, US
House Sparrow
House Sparrow
Eastern Gray Squirrel
Eastern Gray Squirrel

web-edit (previously) is my tiny web app for browsing and editing files in a folder. I mainly built it for editing files on different machines via Tailscale.

I added a "copy to clipboard" option for both text and image data. I needed this because I was running pelican prompts on a laptop from my phone (here's DeepSeek 4.1 Flash 1017) and I wanted an easy way to copy the resulting logs into a Gist.

This research task is really an experiment in running Claude Code for web in "Ultracode" mode, as described in Orchestrate subagents at scale with dynamic workflows in the Claude Code documentation.

With Ultracode turned on, Claude Code makes aggressive use of dynamic workflows - it generates JavaScript that defines and coordinates a network of subagents to take on complex problems.

My initial prompt was this - I meant "hit the rows directly", the word "delete" was an accidental typo on my phone:

Write a SQLite extension in Rust that implants SPARQL and runs really fast because it can hit the delete rows directly

This caused Claude Code for web to churn for over 12 hours. I checked in on its progress the next day and sent a single follow-up prompt when I noticed how many Rust crates it had built:

Every folder in the crates/ directory needs a README

Include a tutorial.md teaching users how to install the extension and how to run it in the SQLite CLI and then teaching them SPARQL in addition to how to use the SQLite extension

Include a script with the tutorial for populating a SQLite database with useful and realistic example data for that tutorial

This project feels like next-level vibe coding - it's not remotely feasible to review the code that was produced here, and even following the documentation is a significant investment in work. I'm not entirely sure what to do with this - I appear to have a full SPARQL implementation in Rust inside SQLite now, but how valuable is that if I created it on a whim and can't (or won't) invest the necessary time to understand what it can do?

Another game experiment - I wanted to see if it was feasible to have a two player game on two separate phones running in static HTML where the networking code uses chirps sent via the microphone. It seems to work!

Build a two player competitive pong like game where two players use their phones close to each other and the game state is sent via audio noises which the web microphone API can interpret - but add graphical and gameplay twists to make it a lot more fun and intertwine than pong

Claude Opus 5.5 Extra in Claude Code.