Example dashboard

Various statistics from my blog.

Owned by simonw, visibility: Public

Entries

3250

SQL query
select 'Entries' as label, count(*) as big_number from blog_entry

Blogmarks

8206

SQL query
select 'Blogmarks' as label, count(*) as big_number from blog_blogmark

Quotations

1301

SQL query
select 'Quotations' as label, count(*) as big_number from blog_quotation

Chart of number of entries per month over time

SQL query
select '<h2>Chart of number of entries per month over time</h2>' as html
SQL query
select to_char(date_trunc('month', created), 'YYYY-MM') as bar_label,
count(*) as bar_quantity from blog_entry group by bar_label order by count(*) desc

Ten most recent blogmarks (of 8206 total)

SQL query
select '## Ten most recent blogmarks (of ' || count(*) || ' total)' as markdown from blog_blogmark
SQL query
select link_title, link_url, commentary, created from blog_blogmark order by created desc limit 10

10 rows

link_title link_url commentary created
firefox parser/html/java/README.txt https://github.com/mozilla-firefox/firefox/tree/main/parser/html/java TIL (or TIR - [Today I was Reminded](https://simonwillison.net/2009/Jul/11/john/)) that the HTML5 Parser used by Firefox is maintained as Java code ([commit history here](https://github.com/mozilla-firefox/firefox/commits/main/parser/html/javasrc)) and converted to C++ using a custom translation script. You can see that in action by checking out the ~8GB Firefox repository and running: cd parser/html/java make sync make translate Here's [a terminal session where I did that](http://gistpreview.github.io/?e53ff836cb44816670adddc3a518b3cc), including the output of `git diff` showing the updated C++ files. I did some digging and found that the code that does the translation work lives, weirdly, in the [Nu Html Checker](https://github.com/validator/validator) repository on GitHub which powers the W3C's [validator.w3.org/nu/](https://validator.w3.org/nu/) validation service! Here's a snippet from [htmlparser/cpptranslate/CppVisitor.java](https://github.com/validator/validator/blob/dfd1948624259c63027bc5953e89bdeee81fb7b0/htmlparser/translator-src/nu/validator/htmlparser/cpptranslate/CppVisitor.java#L421-L442) showing how a class declaration is converted into C++: <pre> <span class="pl-k">protected</span> <span class="pl-smi">void</span> <span class="pl-en">startClassDeclaration</span>() { <span class="pl-s1">printer</span>.<span class="pl-en">print</span>(<span class="pl-s">"#define "</span>); <span class="pl-s1">printer</span>.<span class="pl-en">print</span>(<span class="pl-s1">className</span>); <span class="pl-s1">printer</span>.<span class="pl-en">printLn</span>(<span class="pl-s">"_cpp__"</span>); <span class="pl-s1">printer</span>.<span class="pl-en">printLn</span>(); <span class="pl-k">for</span> (<span class="pl-smi">int</span> <span class="pl-s1">i</span> = <span class="pl-c1">0</span>; <span class="pl-s1">i</span> &lt; <span class="pl-smi">Main</span>.<span class="pl-c1">H_LIST</span>.<span class="pl-s1">length</span>; <span class="pl-s1">i</span>++) { <span class="pl-smi">String</span> <span class="pl-s1">klazz</span> = <span class="pl-smi">Main</span>.<span class="pl-c1">H_LIST</span>[<span class="pl-s1">i</span>]; <span class="pl-k">if</span> (!<span class="pl-s1">klazz</span>.<span class="pl-en">equals</span>(<span class="pl-s1">javaClassName</span>)) { <span class="pl-s1">printer</span>.<span class="pl-en">print</span>(<span class="pl-s">"#include <span class="pl-cce">\"</span>"</span>); <span class="pl-s1">printer</span>.<span class="pl-en">print</span>(<span class="pl-s1">cppTypes</span>.<span class="pl-en">classPrefix</span>()); <span class="pl-s1">printer</span>.<span class="pl-en">print</span>(<span class="pl-s1">klazz</span>); <span class="pl-s1">printer</span>.<span class="pl-en">printLn</span>(<span class="pl-s">".h<span class="pl-cce">\"</span>"</span>); } } <span class="pl-s1">printer</span>.<span class="pl-en">printLn</span>(); <span class="pl-s1">printer</span>.<span class="pl-en">print</span>(<span class="pl-s">"#include <span class="pl-cce">\"</span>"</span>); <span class="pl-s1">printer</span>.<span class="pl-en">print</span>(<span class="pl-s1">className</span>); <span class="pl-s1">printer</span>.<span class="pl-en">printLn</span>(<span class="pl-s">".h<span class="pl-cce">\"</span>"</span>); <span class="pl-s1">printer</span>.<span class="pl-en">printLn</span>(); }</pre> Here's a [fascinating blog post](https://johnresig.com/blog/html-5-parsing/) from John Resig explaining how validator author Henri Sivonen introduced the new parser into Firefox in 2009. 2025-12-17 01:48:54+00:00
The new ChatGPT Images is here https://openai.com/index/new-chatgpt-images-is-here/ OpenAI shipped an update to their ChatGPT Images feature - the feature that [gained them 100 million new users](https://simonwillison.net/2025/May/13/launching-chatgpt-images/) in a week when they first launched it back in March, but has since been eclipsed by Google's Nano Banana and then further by Nana Banana Pro [in November](https://simonwillison.net/2025/Nov/20/nano-banana-pro/). The focus for the new ChatGPT Images is speed and instruction following: > It makes precise edits while keeping details intact, and generates images up to 4x faster It's also a little cheaper: OpenAI say that the new [gpt-image-1.5](https://platform.openai.com/docs/models/gpt-image-1.5) API model makes image input and output "20% cheaper in GPT Image 1.5 as compared to GPT Image 1". I tried a new test prompt against a photo I took of Natalie's ceramic stand at the farmers market a few weeks ago: > Add two kakapos inspecting the pots > > ![Outdoor craft market booth displaying handmade ceramics and jewelry on a navy tablecloth with "NATBAT CREATIONS CALIFORNIA USA" logo. Items include colorful glazed ceramic cups in blue, orange, and black; decorative bowls including a rainbow-striped piece; jewelry pendants and earrings on wooden display stands; ceramic plant markers in various colors labeled "Artichoke", "Cilantro", "Chili", "Oregano", "Potato", "Pumpkin", "Sage".](https://static.simonwillison.net/static/2025/pots-q80-half.jpg) Here's the result from the new ChatGPT Images model: ![Same craft market booth as previous image, now with two large olive-green Kākāpō parrots perched on the table among the ceramics, one investigating the blue glazed cups and the other examining an orange cup.](https://static.simonwillison.net/static/2025/pots-chatgpt-q80-half.jpg) And here's what I got from Nano Banana Pro: ![Same craft market booth with two Kākāpō now in different positions: one remains center-table peering into the ceramic cups near the rainbow pot, while the second has moved to the right edge of the table near the plant markers, appearing to examine or possibly chew on items at the table's corner. They are both a little smaller than in the first image.](https://static.simonwillison.net/static/2025/pots-nano-banana-q80-half.jpg) The ChatGPT Kākāpō are a little chonkier, which I think counts as a win. I was a little less impressed by the result I got for an infographic from the prompt "Infographic explaining how the Datasette open source project works" followed by "Run some extensive searches and gather a bunch of relevant information and then try again" ([transcript](https://chatgpt.com/share/6941f249-cbd0-8006-b9ff-5a19167206bc)): ![Infographic titled "HOW DATASETTE WORKS" with subtitle "THE OPEN SOURCE DATA PLATFORM" showing a four-step workflow. STEP 1 (orange): "LOAD YOUR DATA" - "CSV, JSON, XLSX, SQLite, PostgreSQL, etc." with icons of file types flowing into a laptop. Below: "IMPORT DATASETS - Turn your structured data into SQLite databases and .db files." with checkmarks for "Datasette Desktop App for local deployment", "CLI tool for command-line imports", "Automatic CSV import tool". STEP 2 (green): "PUBLISH & DEPLOY" - "HOST DATASETS ONLINE" with cloud and server icons labeled "DEPLOY". Below: "SHARE ONLINE - Deploy your Datasette instance to a public server." with checkmarks for "Datasette Cloud - Free hosting service", "Deploy anywhere via plugins", "Configurable API tools". STEP 3 (purple): "EXPLORE & QUERY" - "BROWSE, SEARCH & VISUALIZE" with database and browser window icons. Below: "SQL QUERIES & SEARCH - Browse, filter, search, and visualize your data with an interactive web interface." with checkmarks for "Perform SQL queries directly from the browser", "Filter, sort, and facet data", "Generate custom visualizations and charts". STEP 4 (red): "BUILD & EXTEND" - "PLUGINS, APIS & INTEGRATIONS" with gear and wrench icons labeled "API". Below: "CUSTOMIZE & DEVELOP" with bullets "Develop custom plugins for added functionality", "Access JSON API for programmatic queries", "Embed and integrate Datasette into other applications". Bottom banner shows four features: "OPEN DATA PLATFORM - Widely used for visualizing, sharing and building applications with SQLite backed data", "EXTENSIBLE PLUGINS - 100+ plugins available, inc uding chaps, charts authentication, and more", "ACCESS CONTROL - Granular permissions for controlling who s an access and interact with your data", "OPEN SOURCE PROJECT - Actively developed open source project with a vibrant community of contributors".](https://static.simonwillison.net/static/2025/chatgpt-infographic.jpg) See [my Nano Banana Pro post](https://simonwillison.net/2025/Nov/20/nano-banana-pro/#creating-an-infographic) for comparison. Both models are clearly now usable for text-heavy graphics though, which makes them far more useful than previous generations of this technology. 2025-12-16 23:59:22+00:00
s3-credentials 0.17 https://github.com/simonw/s3-credentials/releases/tag/0.17 New release of my [s3-credentials](https://s3-credentials.readthedocs.io/) CLI tool for managing credentials needed to access just one S3 bucket. Here are the release notes in full: > - New commands `get-bucket-policy` and `set-bucket-policy`. [#91](https://github.com/simonw/s3-credentials/issues/91) > - New commands `get-public-access-block` and `set-public-access-block`. [#92](https://github.com/simonw/s3-credentials/issues/92) > - New `localserver` command for starting a web server that makes time limited credentials accessible via a JSON API. [#93](https://github.com/simonw/s3-credentials/pull/93) That `s3-credentials localserver` command ([documented here](https://s3-credentials.readthedocs.io/en/stable/localserver.html)) is a little obscure, but I found myself wanting something like that to help me test out a new feature I'm building to help create temporary Litestream credentials using Amazon STS. Most of that new feature was [built by Claude Code](https://gistpreview.github.io/?500add71f397874ebadb8e04e8a33b53) from the following starting prompt: > `Add a feature s3-credentials localserver which starts a localhost weberver running (using the Python standard library stuff) on port 8094 by default but -p/--port can set a different port and otherwise takes an option that names a bucket and then takes the same options for read--write/read-only etc as other commands. It also takes a required --refresh-interval option which can be set as 5m or 10h or 30s. All this thing does is reply on / to a GET request with the IAM expiring credentials that allow access to that bucket with that policy for that specified amount of time. It caches internally the credentials it generates and will return the exact same data up until they expire (it also tracks expected expiry time) after which it will generate new credentials (avoiding dog pile effects if multiple requests ask at the same time) and return and cache those instead.` 2025-12-16 23:40:31+00:00
ty: An extremely fast Python type checker and LSP https://astral.sh/blog/ty The team at Astral have been working on this for quite a long time, and are finally releasing the first beta. They have some big performance claims: > Without caching, ty is consistently between 10x and 60x faster than mypy and Pyright. When run in an editor, the gap is even more dramatic. As an example, after editing a load-bearing file in the PyTorch repository, ty recomputes diagnostics in 4.7ms: 80x faster than Pyright (386ms) and 500x faster than Pyrefly (2.38 seconds). ty is very fast! The easiest way to try it out is via `uvx`: cd my-python-project/ uvx ty check I [tried it](https://gistpreview.github.io/?a3aff6768e85168d89d4515e3dbcb7d2) against [sqlite-utils](https://sqlite-utils.datasette.io/) and it turns out I have quite a lot of work to do! Astral also released a new [VS Code extension](https://marketplace.visualstudio.com/items?itemName=astral-sh.ty) adding ty-powered language server features like go to definition. I'm still getting my head around how this works and what it can do. 2025-12-16 23:35:33+00:00
Poe the Poet https://poethepoet.natn.io/ I was looking for a way to specify additional commands in my `pyproject.toml` file to execute using `uv`. There's an [enormous issue thread](https://github.com/astral-sh/uv/issues/5903) on this in the `uv` issue tracker (300+ comments dating back to August 2024) and from there I learned of several options including this one, Poe the Poet. It's neat. I added it to my [s3-credentials](https://github.com/simonw/s3-credentials) project just now and the following now works for running the live preview server for the documentation: uv run poe livehtml Here's the snippet of TOML I added to my `pyproject.toml`: <pre>[<span class="pl-en">dependency-groups</span>] <span class="pl-smi">test</span> = [ <span class="pl-s"><span class="pl-pds">"</span>pytest<span class="pl-pds">"</span></span>, <span class="pl-s"><span class="pl-pds">"</span>pytest-mock<span class="pl-pds">"</span></span>, <span class="pl-s"><span class="pl-pds">"</span>cogapp<span class="pl-pds">"</span></span>, <span class="pl-s"><span class="pl-pds">"</span>moto&gt;=5.0.4<span class="pl-pds">"</span></span>, ] <span class="pl-smi">docs</span> = [ <span class="pl-s"><span class="pl-pds">"</span>furo<span class="pl-pds">"</span></span>, <span class="pl-s"><span class="pl-pds">"</span>sphinx-autobuild<span class="pl-pds">"</span></span>, <span class="pl-s"><span class="pl-pds">"</span>myst-parser<span class="pl-pds">"</span></span>, <span class="pl-s"><span class="pl-pds">"</span>cogapp<span class="pl-pds">"</span></span>, ] <span class="pl-smi">dev</span> = [ {<span class="pl-smi">include-group</span> = <span class="pl-s"><span class="pl-pds">"</span>test<span class="pl-pds">"</span></span>}, {<span class="pl-smi">include-group</span> = <span class="pl-s"><span class="pl-pds">"</span>docs<span class="pl-pds">"</span></span>}, <span class="pl-s"><span class="pl-pds">"</span>poethepoet&gt;=0.38.0<span class="pl-pds">"</span></span>, ] [<span class="pl-en">tool</span>.<span class="pl-en">poe</span>.<span class="pl-en">tasks</span>] <span class="pl-smi">docs</span> = <span class="pl-s"><span class="pl-pds">"</span>sphinx-build -M html docs docs/_build<span class="pl-pds">"</span></span> <span class="pl-smi">livehtml</span> = <span class="pl-s"><span class="pl-pds">"</span>sphinx-autobuild -b html docs docs/_build<span class="pl-pds">"</span></span> <span class="pl-smi">cog</span> = <span class="pl-s"><span class="pl-pds">"</span>cog -r docs/*.md<span class="pl-pds">"</span></span></pre> Since `poethepoet` is in the `dev=` dependency group any time I run `uv run ...` it will be available in the environment. 2025-12-16 22:57:02+00:00
2025 Word of the Year: Slop https://www.merriam-webster.com/wordplay/word-of-the-year Slop lost to "brain rot" for [Oxford Word of the Year 2024](https://simonwillison.net/2024/Nov/15/slop-word-of-the-year/) but it's finally made it this year thanks to Merriam-Webster! > Merriam-Webster’s human editors have chosen slop as the 2025 Word of the Year. We define slop as “digital content of low quality that is produced usually in quantity by means of artificial intelligence.” 2025-12-15 17:27:59+00:00
Copywriters reveal how AI has decimated their industry https://www.bloodinthemachine.com/p/i-was-forced-to-use-ai-until-the Brian Merchant has been collecting personal stories for his series [AI Killed My Job](https://www.bloodinthemachine.com/s/ai-killed-my-job) - previously covering [tech workers](https://www.bloodinthemachine.com/p/how-ai-is-killing-jobs-in-the-tech-f39), [translators](https://www.bloodinthemachine.com/p/ai-killed-my-job-translators), and [artists](https://www.bloodinthemachine.com/p/artists-are-losing-work-wages-and) - and this latest piece includes anecdotes from 12 professional copywriters all of whom have had their careers devastated by the rise of AI-generated copywriting tools. It's a tough read. Freelance copywriting does not look like a great place to be right now. > AI is really dehumanizing, and I am still working through issues of self-worth as a result of this experience. When you go from knowing you are valuable and valued, with all the hope in the world of a full career and the ability to provide other people with jobs... To being relegated to someone who edits AI drafts of copy at a steep discount because “most of the work is already done” ... The big question for me is if a new AI-infested economy creates new jobs that are a great fit for people affected by this. I would hope that clear written communication skills are made even more valuable, but the people interviewed here don't appear to be finding that to be the case. 2025-12-14 05:06:19+00:00
LLM 0.28 https://llm.datasette.io/en/stable/changelog.html#v0-28 I released a new version of my [LLM](https://llm.datasette.io/) Python library and CLI tool for interacting with Large Language Models. Highlights from the release notes: > - New OpenAI models: `gpt-5.1`, `gpt-5.1-chat-latest`, `gpt-5.2` and `gpt-5.2-chat-latest`. [#1300](https://github.com/simonw/llm/issues/1300), [#1317](https://github.com/simonw/llm/issues/1317) > - When fetching URLs as fragments using `llm -f URL`, the request now includes a custom user-agent header: `llm/VERSION (https://llm.datasette.io/)`. [#1309](https://github.com/simonw/llm/issues/1309) > - Fixed a bug where fragments were not correctly registered with their source when using `llm chat`. Thanks, [Giuseppe Rota](https://github.com/grota). [#1316](https://github.com/simonw/llm/pull/1316) > - Fixed some file descriptor leak warnings. Thanks, [Eric Bloch](https://github.com/eedeebee). [#1313](https://github.com/simonw/llm/issues/1313) > - Type annotations for the OpenAI Chat, AsyncChat and Completion `execute()` methods. Thanks, [Arjan Mossel](https://github.com/ar-jan). [#1315](https://github.com/simonw/llm/pull/1315) > - The project now uses `uv` and dependency groups for development. See the updated [contributing documentation](https://llm.datasette.io/en/stable/contributing.html). [#1318](https://github.com/simonw/llm/issues/1318) That last bullet point about `uv` relates to the dependency groups pattern I [wrote about in a recent TIL](https://til.simonwillison.net/uv/dependency-groups). I'm currently working through applying it to my other projects - the net result is that running the test suite is as simple as doing: git clone https://github.com/simonw/llm cd llm uv run pytest The new `dev` dependency group [defined in pyproject.toml](https://github.com/simonw/llm/blob/0.28/pyproject.toml#L44-L69) is automatically installed by `uv run` in a new virtual environment which means everything needed to run `pytest` is available without needing to add any extra commands. 2025-12-12 20:20:14+00:00
The Normalization of Deviance in AI https://embracethered.com/blog/posts/2025/the-normalization-of-deviance-in-ai/ This thought-provoking essay from Johann Rehberger directly addresses something that I’ve been worrying about for quite a while: in the absence of any headline-grabbing examples of prompt injection vulnerabilities causing real economic harm, is anyone going to care? Johann describes the concept of the “Normalization of Deviance” as directly applying to this question. Coined by [Diane Vaughan](https://en.wikipedia.org/wiki/Diane_Vaughan), the key idea here is that organizations that get away with “deviance” - ignoring safety protocols or otherwise relaxing their standards - will start baking that unsafe attitude into their culture. This can work fine… until it doesn’t. The Space Shuttle Challenger disaster has been partially blamed on this class of organizational failure. As Johann puts it: > In the world of AI, we observe companies treating probabilistic, non-deterministic, and sometimes adversarial model outputs as if they were reliable, predictable, and safe. > > Vendors are normalizing trusting LLM output, but current understanding violates the assumption of reliability. > > The model will not consistently follow instructions, stay aligned, or maintain context integrity. This is especially true if there is an attacker in the loop (e.g indirect prompt injection). > > However, we see more and more systems allowing untrusted output to take consequential actions. Most of the time it goes well, and over time vendors and organizations lower their guard or skip human oversight entirely, because “it worked last time.” > > This dangerous bias is the fuel for normalization: organizations confuse the absence of a successful attack with the presence of robust security. 2025-12-10 20:18:58+00:00
10 Years of Let's Encrypt https://letsencrypt.org/2025/12/09/10-years Internet Security Research Group co-founder and Executive Director Josh Aas: > On September 14, 2015, [our first publicly-trusted certificate went live](https://crt.sh/?id=9314793). [...] Today, Let’s Encrypt is the largest certificate authority in the world in terms of certificates issued, the ACME protocol we helped create and standardize is integrated throughout the server ecosystem, and we’ve become a household name among system administrators. We’re closing in on protecting one billion web sites. Their growth rate and numbers are wild: > In March 2016, we issued our one millionth certificate. Just two years later, in September 2018, we were issuing a million certificates every day. In 2020 we reached a billion total certificates issued and as of late 2025 we’re frequently issuing ten million certificates per day. According to [their stats](https://letsencrypt.org/stats/) the amount of Firefox traffic protected by HTTPS doubled from 39% at the start of 2016 to ~80% today. I think it's difficult to over-estimate the impact Let's Encrypt has had on the security of the web. 2025-12-10 00:34:15+00:00
Copy and export data

Duration: 3.34ms