Datasette adds writes, Willison lazy-loads GIFs, Qwen3.6-27B codes locally
Solo maintainers drive today's AI-tech ships: Willison consolidates Datasette write UI plus a GIF facade, Gerganov picks Qwen3.6 over Gemma 4.
Datasette adds writes, Willison lazy-loads GIFs, Qwen3.6-27B codes locally
TL;DR
- Datasette 1.0a34 folds write UI from plugins into the core table interface.
- datasette-tailscale embeds a Tailscale node with WireGuard keys stored in plaintext.
<click-to-play>swaps heavy GIFs for a still + play button until reader click.- Qwen3.6-27B posts 77.2 on SWE-bench Verified at ~18GB VRAM on Gerganov’s pi harness.
- Brent Simmons polishes open-source NetNewsWire full-time a year into retirement.
Today’s AI-tech section is solo-maintainer work end to end, with no shared product thread. Datasette 1.0a34 pulls two years of datasette-write-ui plugin work into the core table UI, then ships a separate datasette-tailscale sidecar whose Rust bindings park WireGuard keys in plaintext — a deliberately unstable networking surface flagged as such. Simon Willison’s <click-to-play> Web Component swaps heavy GIFs for a still + play button until the reader clicks, with critics noting <video preload="none"> would cut payloads 70–90% without JavaScript. And Georgi Gerganov is running Qwen3.6-27B as his daily coding driver on Mario Zechner’s 418-line pi harness, against a June consensus that points at Gemma 4 and GPT-OSS instead.
A round-up on Brent Simmons turning retirement into full-time NetNewsWire polish closes the section.
Datasette folds write UI into core, ships Tailscale sidecar
Source: simon-willison · published 2026-06-16
TL;DR
- Datasette 1.0a34 moves insert/edit/delete out of plugin-land and into the core table UI, retiring Alex Garcia’s
datasette-write-uiprototype. - The release adds
datasette.allowed_many()and per-request permission caching — necessary because every row now fans out multiple permission checks. - A companion
datasette-tailscaleplugin embeds a Tailscale node in-process via experimental Rust bindings that store WireGuard keys in plaintext. - Same-day drop reads as Willison consolidating two years of plugin work into core while opening a new, openly-unstable networking frontier.
The write UI was the easy part; the permissions plumbing wasn’t
The headline of Datasette 1.0a34 is that you can now insert, edit, and delete rows from the table page without dropping to SQL or installing a plugin. Willison frames the gap as “absurd” given that Datasette Agent already exposed the same operations through a chat interface. Fair — but the more interesting shipped code isn’t the forms.
Secondary coverage flags two additions the annotated release notes underplay: a new datasette.allowed_many() batch permission API, and per-request permission caching to stop the UI from stalling when multiple plugins each do their own lookups 1. Both exist because every row on a table page now triggers insert/edit/delete action-item checks, and the naive path would make a 100-row page feel like a 100-RPC page. Treat 1.0a34 less as “we added forms” and more as a stress test of the permissions system ahead of 1.0 stable.
The release also quietly retires community work. Alex Garcia’s datasette-write-ui, built in August 2023 to power Datasette Cloud, was the prototype for what’s now in core 2. The Cloud-plugin-first, core-absorption-later pattern is becoming a recognizable Datasette release rhythm, and the “rich plugin ecosystem” framing obscures that one of the more useful plugins just got absorbed.
datasette-tailscale: a public lab notebook, not a production tool
The companion drop is architecturally bolder and operationally scarier. datasette-tailscale embeds a Tailscale node directly in the Datasette process via tailscale-rs, a Rust reimplementation of tsnet with Python bindings, rather than depending on a system tailscaled 3. Willison openly notes the proxy bridge between the embedded node and the local ASGI app is provisional and has filed upstream issues asking for a cleaner in-process listener 3.
The caveats deserve to be quoted, not glossed. tailscale-rs refuses to import unless you set TS_RS_EXPERIMENT=this_is_unstable_software, and it stores auth keys and private WireGuard keys in plaintext on disk 4. That’s a meaningful asterisk on any “securely expose your database over your tailnet” pitch. Independent Tailscale users also report periodic re-auth interruptions that break long-running services unless persistent auth keys are configured 5 — a wrinkle a one-command datasette tailscale workflow doesn’t yet handle.
Where this lands
A 2026 no-code roundup positions the new write UI as the moment Datasette becomes a credible self-hosted alternative to Airtable and NocoDB, singling out the custom-column-type hook — plugins can supply JS editors for domain-specific fields — as the differentiator a closed field system can’t match 6. The flat-cost self-hosting story still comes with real operational burden compared to NocoDB’s spreadsheet polish, but the gap narrows meaningfully with 1.0a34.
Read together, the same-day drop is two things at once: production-track consolidation of plugin experiments into core, and a deliberately-flagged experimental frontier on embedded networking. Don’t conflate them. Run the write UI; read the Tailscale plugin.
Further reading
- datasette-tailscale 0.1a0 — simon-willison
Willison’s lazy-loads GIFs on user click
Source: simon-willison · published 2026-06-17
TL;DR
- Simon Willison shipped
<click-to-play>, a Web Component that swaps heavy GIFs for a still + play button until the reader clicks. - The pattern follows Paul Irish’s
lite-youtube-embedfacade, which renders up to 224× faster than a 1.3–2.6 MB iframe. - Naive uses can trigger the browser’s preload scanner, fetching the GIF before the component’s JS runs and defeating the point.
- Critics argue the real fix is
<video preload="none">, which cuts payloads 70–90% and gets native lazy-loading for free.
What the component does
Willison’s <click-to-play> wraps an <a href="big.gif"> around an <img src="first-frame.jpg"> and upgrades the pair into a poster-with-play-button. Click it and the GIF loads on demand. No shadow DOM, no dependencies, ~50 lines. He built it for a Datasette post that needed several screencasts on one page without blowing the initial payload.
The progressive-enhancement story is the appeal: with JS disabled the markup degrades to a normal linked image. Crawlers and screen readers see standard HTML. It’s the kind of small, single-file utility that fits his tools.simonwillison.net collection of LLM-generated drop-ins, where Web Components are his chosen encapsulation specifically because AI-written snippets need to survive being pasted into arbitrary pages without leaking state 7.
Not a new idea — and that’s fine
The “facade” pattern has a decade of prior art. Paul Irish’s lite-youtube-embed is the canonical case: replace a 1.3–2.6 MB iframe with a thumbnail shell, render up to 224× faster, pre-warm the connection on hover 8. Labnol shipped a similar “Lite YouTube” script in 2013. Willison’s contribution is narrower — GIF-specific, no hover prefetch — but the lineage is honest web-perf territory, not novelty.
It also lines up with WCAG 2.2.2 (Pause, Stop, Hide), which requires a pause mechanism for any motion lasting more than five seconds 9. A bare autoplay GIF fails that test. A click-to-play wrapper passes it almost by accident.
The preload-scanner trap
The subtle failure mode is the browser’s speculative preload scanner. Because Willison keeps a real <img src="…"> in the light DOM for the no-JS fallback, the scanner sees it and fetches the still frame before the component’s JS initializes 10 — that’s the intended behavior and it’s fine.
flowchart LR
A[HTML parsed] --> B{img src points to?}
B -->|still frame .jpg| C[Small fetch ✓<br/>component upgrades]
B -->|the heavy .gif| D[Preload scanner<br/>commits download ✗]
D --> E[Lazy-loading defeated<br/>before JS runs]
But a consumer who points src directly at the GIF — a natural mis-use, since the markup looks like it should “just work” either way — gets the worst of both worlds: the scanner commits to the multi-megabyte download immediately, and the play button becomes decorative 10. Fixes exist (<template> tags, data-src rewriting) but each one trades away the no-JS fallback Willison explicitly wants.
Or: stop shipping GIFs
The harder critique is that <click-to-play> is solving the wrong problem. A 10-second GIF can hit 20 MB; the MP4 equivalent is typically under 2 MB 11. A <video preload="none" poster="first-frame.jpg" muted loop playsinline> delivers the same still-that-plays UX with an order-of-magnitude smaller payload, and modern browsers honor loading="lazy" on <video> to skip the request entirely until it’s near the viewport 12.
By that standard, <click-to-play> is a pragmatic patch for content you already have in GIF form — Datasette screencasts, embedded social clips, archive material — rather than the default tool for new captures. Convert the source to muted looping video and the wrapper becomes unnecessary.
That’s not a knock on the component. It’s a reminder that the cheapest byte is the one you never encode as a GIF in the first place.
Gerganov runs Qwen3.6-27B daily on a 418-line pi agent
Source: simon-willison · published 2026-06-16
TL;DR
- Qwen3.6-27B posts 77.2 on SWE-bench Verified and 59.3 on Terminal-Bench 2.0 at ~18GB VRAM, beating its own 397B MoE predecessor.
- Gerganov’s harness is Mario Zechner’s pi — 4 tools, sub-1,000-token system prompt, ~418 lines of TypeScript, no MCP or sub-agents.
- ggml-org commits carry an
Assisted-by:trailer rather thanCo-authored-by:, preserving human legal authorship. - June 2026’s consensus local-coding picks are Gemma 4 and GPT-OSS, making Gerganov’s Qwen vote a minority one.
A maintainer’s stack, not a marketing claim
When the author of llama.cpp says a 27B model is good enough for his daily work, the signal is worth more than another benchmark blog. Georgi Gerganov reports running Qwen3.6-27B “almost daily” on an M2 Ultra and an RTX 5090, doing small maintenance tasks across the ggml-org repos with a stripped-down harness (pi -nc --offline) and a short style-aligning system prompt.
Independent reviews back the capability claim with numbers: 77.2 on SWE-bench Verified, 59.3 on Terminal-Bench 2.0, fitting in roughly 18GB VRAM at 4-bit — outperforming Qwen’s own 397B MoE predecessor 13. The hardware split matters too. On the 5090, vLLM with multi-token prediction hits 100–150 t/s; the M2 Ultra manages 36–40 t/s, but its 192GB unified memory loads 120B+ models the 5090’s 32GB can’t touch 14. Two rigs, two roles.
The harness is half the story
The pi agent deserves more attention than the model. Mario Zechner exposes exactly four tools — read, write, edit, bash — behind a sub-1,000-token system prompt, with a core loop of ~418 lines of TypeScript 15. There is no plan mode, no sub-agents, no built-in MCP. That is the opposite direction from Claude Code and Cursor, and it is precisely why a maintainer trusts running it offline against a local checkpoint.
flowchart LR
M[Qwen3.6-27B<br/>local GGUF] --> P[pi agent<br/>~418 LOC]
S[SYSTEM.md<br/><1k tokens] --> P
P --> T[4 tools:<br/>read/write/edit/bash]
T --> R[ggml-org repo]
R -->|commit| C["Assisted-by:<br/>pi:llama.cpp/MODEL"]
H[Human review] --> C
The policy wrapper around it is equally deliberate. ggml-org commits use an Assisted-by: trailer rather than Co-authored-by: — a legal distinction, not a cosmetic one — and AGENTS.md bans predominantly AI-generated PRs and fully autonomous agents from contributing 16. Gerganov’s endorsement is therefore narrower than the quote reads: AI for mundane tasks, human sign-off retained on every commit.
The caveats Gerganov skipped
Practitioners on r/LocalLLM warn that the rosy benchmarks assume quantization discipline. Heavy 4-bit quants “lobotomize” tool-calling on the MoE variants; Q5 or Q6 is the floor for reliable agentic use 17. Reviewers also flag confident hallucinations of APIs for niche libraries unless the prompt explicitly instructs “if unknown, say unknown” 13.
And there’s a bigger asterisk: Qwen may not even be the headline local model of June 2026. Vicki Boykis’s “Running local models is good now” post — the 900-point HN thread Gerganov was commenting on — frames Gemma 4 26B-A4B and OpenAI’s GPT-OSS as the breakthrough wins, with Gemma 4 31B edging the original GPT-4 on MMLU (87.1 vs 86.5) 18.
What’s actually load-bearing
Strip the model-of-the-month framing and what’s left is a working pattern: a mid-size open-weights model, a deliberately tiny agent, an offline flag, and a commit policy that keeps a human in the legal loop. The Qwen-vs-Gemma argument will be obsolete in six weeks. The shape of the stack probably won’t be.
Round-ups
Brent Simmons turns retirement into polishing NetNewsWire
Source: simon-willison
NetNewsWire, the open-source RSS reader first released in 2002, is now Brent Simmons’ full-time retirement project a year after he stopped working commercially. The Mac and iPhone client, open-sourced in 2018, gets undivided attention free of business pressure.
Footnotes
-
letsdatascience.com (secondary coverage) — https://letsdatascience.com/news/datasette-publishes-new-10a34-release-for-data-exploration-1a8b584d
↩A new method, datasette.allowed_many(), was introduced to handle multiple permission checks simultaneously… 1.0a34 implements per-request permission caching to prevent the UI from becoming sluggish when multiple plugins perform permission lookups.
-
simonwillison.net (datasette-cloud tag) — https://simonwillison.net/tags/datasette-cloud/
↩datasette-write-ui was developed by Alex Garcia in August 2023, initially built to support Datasette Cloud — it served as the prototype for handling authentication and UI elements for the underlying Write API.
-
Simon Willison — datasette-tailscale 0.1a0 post — https://simonwillison.net/2026/Jun/16/datasette-tailscale/
↩ ↩2Uses Python bindings for tailscale-rs, an experimental Rust-based reimplementation of the Tailscale tsnet library… I’m still looking for a cleaner way to handle the proxy between the embedded node and the local ASGI app.
-
simonw GitHub gist (tailscale-rs experiment notes) — https://gist.github.com/simonw/b6dbb230d755c33490087581821d7082
↩Developers must explicitly set TS_RS_EXPERIMENT=this_is_unstable_software as a safety gate; sensitive state including auth keys and private WireGuard keys is currently stored in plaintext on disk.
-
Product Hunt — Tailscale reviews — https://www.producthunt.com/products/tailscale/reviews
↩Users report friction with Tailscale’s periodic re-authentication requirements, which can interrupt long-running background services if not managed through persistent auth keys.
-
sheikhshadi.com — 2026 no-code tools roundup — https://sheikhshadi.com/blogs/best-no-code-tools/
↩Datasette’s new native editing UI respects custom column types and uses JavaScript plugins to supply specialized editors… open-source alternatives like NocoDB and Datasette offer a flat cost structure via self-hosting, though they require more technical expertise to maintain.
-
Simon Willison — Vibe engineering — https://simonwillison.net/2025/Oct/7/vibe-engineering/
↩Willison frequently leverages Web Components to encapsulate functionality, ensuring that these AI-generated snippets remain functional across different environments without breaking global styles or logic.
-
OpenReplay — lite-youtube-embed facade pattern — https://blog.openreplay.com/embedding-youtube-videos/
↩A standard YouTube iframe embed typically requests between 1.3 MB and 2.6 MB of JavaScript and CSS assets… [the facade] renders up to 224 times faster than the native player while maintaining a ‘warm’ connection through prefetching on hover.
-
Cloudfour — Accessible animated GIF alternatives — https://cloudfour.com/thinks/accessible-animated-gif-alternatives/
↩WCAG 2.2.2 (Pause, Stop, Hide)… requires that any moving content lasting longer than five seconds must include a mechanism for the user to pause or stop it.
-
PerfPlanet — The Big Bad Preloader (preload scanner) — https://calendar.perfplanet.com/2013/big-bad-preloader/
↩ ↩2When a developer places a fallback
tag inside the light DOM of a web component… the preload scanner identifies the src attribute and initiates a request immediately… before the web component’s JavaScript can initialize or modify the DOM.
-
Squarespace Engineering — HTML video lazy loading — https://engineering.squarespace.com/blog/2026/how-to-use-standard-html-video-and-audio-lazy-loading-on-the-web-today
↩A 10-second GIF might reach 20MB, while the MP4 equivalent is often 2MB or less… a properly configured
-
ImageKit — Lazy loading HTML videos — https://imagekit.io/blog/lazy-loading-html-videos/
↩Modern browsers now support loading=“lazy” for video tags, which prevents the browser from even initiating the request—even if set to autoplay—until the video is near the user’s viewport.
-
Build Fast with AI — Qwen3.6-27B review — https://www.buildfastwithai.com/blogs/qwen3-6-27b-review-2026
↩ ↩2Qwen3.6-27B scores 77.2 on SWE-bench Verified and 59.3 on Terminal-Bench 2.0, outperforming its own 397B MoE predecessor while fitting in ~18GB VRAM at 4-bit — but hallucinates APIs for niche libraries unless prompted with ‘if unknown, say unknown’.
-
YouTube benchmark — RTX 5090 vs M2 Ultra on Qwen3.6-27B — https://www.youtube.com/watch?v=Dli5slNaJu0
↩RTX 5090 hits 100–150 t/s with vLLM+MTP (50–60 t/s on plain 4-bit GGUF) versus 36–40 t/s on M2 Ultra; the 5090’s 32GB VRAM caps context, while M2 Ultra’s 192GB unified memory scales to 120B+ models the 5090 can’t load.
-
Mario Zechner (pi agent author) — mariozechner.at — https://mariozechner.at/
↩Pi exposes only four tools — read, write, edit, bash — with a <1,000-token system prompt and ~418 lines of TypeScript in the core loop; the design rejects ‘plan mode’, sub-agents, and built-in MCP as unnecessary bloat.
-
YouTube walkthrough — pi agent + llama.cpp workflow — https://www.youtube.com/watch?v=HT2NplHskIQ
↩ggml-org enforces an ‘Assisted-by: pi:llama.cpp/[MODEL]’ commit trailer rather than Co-authored-by, and the AGENTS.md policy bans predominantly AI-generated PRs and fully autonomous agents from contributing.
-
r/LocalLLM thread on Qwen3.6-27B — https://www.reddit.com/r/LocalLLM/comments/1t3pjkn/qwen3627b_is_the_first_local_model_that_actually/
↩Qwen3.6-27B is the first local model that actually [works for agentic coding] — but heavy 4-bit quantization noticeably degrades tool-calling; users recommend at least Q5 or Q6 for MoE variants to avoid a ‘brain lobotomy’.
-
gemma4-ai.com — Gemma 4 vs GPT-4 benchmark — https://gemma4-ai.com/blog/gemma4-vs-gpt4
↩Gemma 4 31B dense scores 87.1% on MMLU vs original GPT-4’s 86.5%, and Boykis’s ‘Running local models is good now’ post (900+ HN points) frames Gemma 4 26B-A4B and GPT-OSS — not Qwen — as the headline local-coding wins of June 2026.