Latest Posts (20 found)

Hello Python 3.15, goodbye 3.10, and now I can use 3.11

Python 3.15 is here ! Here's the essential What's new in Python 3.15 guide, this year curated by 3.14 and 3.15 release manager Hugo van Kemenade. As someone who builds open source software that works in every supported Python version, I get to say goodbye to Python 3.10 and start embracing features from Python 3.11. First, some highlights from Python 3.15. I'm most excited about Explicit lazy imports from PEP 810 . These let you import modules like so: The module will then be imported the first time those symbols are used in the code that follows. My LLM CLI tool implements plugins. I had to recommend manual lazy imports in the plugin author documentation, because simply installing plugins that used heavy dependencies such as could add multiple seconds to every invocation of the command. Lazy imports directly address this startup cost. The new unpacking syntax is neat. Here's an illustration lifted from the documentation : PEP 798: Unpacking in Comprehensions by Adam Hartz and Erik Demaine proposed this as an extension of the unpacking syntax from PEP 448 , which landed in Python 3.5 back in 2015. Python's existing tracing profiler has been renamed from to profiling.tracing , and has been joined by an exciting new sampling profiler called Tachyon, aka profiling.sampling . Sampling profilers work by periodically capturing stack traces from running processes, then using those to calculate which functions have the most time spent inside of them. They're very low overhead and can provide a deep insight into running systems, without needing to instrument those systems first. I've previously used tools like py-spy for this. Now this capability is baked into the standard library. If you have the PID of an existing Python 3.15 process you can start gathering samples by running this (I had to use on macOS): Interact with the main program a bit, then hit Ctrl+C to stop gathering and see a report, which looks something like this: It can also produce flame graphs like this and heat maps like this , and can even output a format that can be viewed in the Firefox profiler UI. Miguel Grinberg investigated How fast is Python 3.15? the other day against the 3.15.0rc3 release and found performance to be mostly the same as 3.14, with the exception of the experimental JIT. The JIT gave a 1.2x boost over the standard 3.15 interpreter (on a Fibonacci test), the first time Miguel observed a meaningful JIT performance increase. Miguel's explorations of 3.14 last year found significant boosts against Python 3.13 across many more categories. I used the following sequence of commands to get to use Python 3.15 on my Mac. First I had to upgrade itself to ensure it knew about the new release: Then I installed the stable 3.15 release: And told that I wanted that to be the default for every command, unless the project itself pinned a different version: Now the following reports 3.15 as the version: I already have a bunch of tools installed using . I reinstalled them to use Python 3.15 like this: Then I can confirm the version like this: The release of 3.15 means that Python 3.10 (released in 2021) is officially no longer supported . My policy for my main open source projects is to keep them working in every supported Python version. I can now drop 3.10 and start using features that were introduced in 3.11. Here's the What's new in Python 3.11 document. I finally get to rely on tomllib being available! Datasette makes extensive use of . 3.11 adds TaskGroup and timeout() , and several more new features. is a new in 3.11 alias for . Python's SQLite support got some significant upgrades in 3.11, including: Plus a whole bunch of improvements to Python's typing system . I plan to land Python 3.11 improvements to a bunch of my projects over the next few weeks. Here's my first Datasette PR removing that workaround. I've also updated my click-app , datasette-plugin , python-lib , and llm-plugin cookiecutter templates to test against Python 3.11 through Python 3.15. You are only seeing the long-form articles from my blog. Subscribe to /atom/everything/ to get all of my posts, or take a look at my other subscription options . setlimit() and getlimit() expose a mechanism for setting SQLite runtime limits from Python. create_window_function() lets us create custom window functions in Python, as a class with , , , and methods. blobopen() provides an efficient mechanism for partial reads and writes of large binary values stored in a SQLite row. You can now call to remove a SQLite authorizer. Datasette had a workaround for this!

0 views
Unsung Today

“They take time. Put in the time.”

À propos the last post , from music editor Marc Weidenbaum : The first is don’t let anyone or anything, or anything acting like anyone, or anyone promoting anything, convince you that craft is outdated or will ever be outdated; you will only improve, only make the sounds you imagine in your mind’s ear and feel in yourself, by working steadily to achieve them. Also recognize that expected paths lead to unlikely ends the more patiently and curiously, guided by instinct and experience, you walk those paths. There is always an excuse to not put in the work. If you allow “AI” (I put it in quotes because it’s less a specific thing, in this context, than what is often cast as, to borrow a historical term, a specter) to become that excuse, well, it’s unfortunate, but there would have been other excuses. So, the best thing to do is put the threat you may perceive from AI in a tidy little box full of all those other excuses, don’t distinguish it from them, and get to work. […] I’ll close now as I opened, with the bad news. These two suggestions, you may have noted, have something in common: they don’t happen instantly, at the push of a button. They take time. Put in the time. (Quote discovered via Robin Sloan’s newsletter .)

0 views
Unsung Today

“I’m ready to throw up at any moment.”

I linked to his work before , and here’s The Art Of Storytelling with another interesting video – about what made Jobs such a good presenter: = 2x) and (width >= 700px)" srcset="https://unsung.aresluna.org/_media/im-ready-to-throw-up-at-any-moment/yt1-play.2096w.avif" type="image/avif"> = 3x) or (width >= 700px)" srcset="https://unsung.aresluna.org/_media/im-ready-to-throw-up-at-any-moment/yt1-play.1600w.avif" type="image/avif"> I’m not going to spoil the findings, which are presented well with good corroborating examples. But the most interesting thing to me was one short video snippet of Jobs being visibly nervous before a very early, 1978 presentation: = 2x) and (width >= 700px)" srcset="https://unsung.aresluna.org/_media/im-ready-to-throw-up-at-any-moment/1.2096w.avif" type="image/avif"> = 3x) or (width >= 700px)" srcset="https://unsung.aresluna.org/_media/im-ready-to-throw-up-at-any-moment/1.1600w.avif" type="image/avif"> It made me think about other examples of Jobs being off his presenting game. The most well-known one is likely the 2005 announcement of the “iTunes phone,” or Motorola ROKR E1 , just over a year before the iPhone. It’s a really painful watch, seeing Jobs squint at the sort of interface he usually makes fun of, regretting the whole deal, very aware of the smartphone they themselves are working on, but unable to speak about it yet (the relevant part starts at 17:24): = 2x) and (width >= 700px)" srcset="https://unsung.aresluna.org/_media/im-ready-to-throw-up-at-any-moment/yt2-play.2096w.avif" type="image/avif"> = 3x) or (width >= 700px)" srcset="https://unsung.aresluna.org/_media/im-ready-to-throw-up-at-any-moment/yt2-play.1600w.avif" type="image/avif"> But I have another, trickier suggestion: the “Antennagate” presentation of 2010 , where Apple had to do damage control over the issue of the iPhone 4 losing cell signal when gripped a certain way: = 2x) and (width >= 700px)" srcset="https://unsung.aresluna.org/_media/im-ready-to-throw-up-at-any-moment/yt3-play.2096w.avif" type="image/avif"> = 3x) or (width >= 700px)" srcset="https://unsung.aresluna.org/_media/im-ready-to-throw-up-at-any-moment/yt3-play.1600w.avif" type="image/avif"> Now, this isn’t a bad presentation. I actually think it is a great one. The arguments are valid, the data solid, and the whole thing comes together well. In the recently leaked and generally rarely witnessed slide-less Q&A portion, Jobs displays understanding of the issue and answers questions well; perhaps I’m grading on a curve here, but even his one moment of anger/​frustration seems so much better handled than the famously impatient and immature Jobs would have done earlier in his career. Both videos are worth studying and teach how to do damage control well, and how to tell stories of things you would otherwise choose never to talk about. But also, early on, it was really fascinating to see Jobs reading off of so many generic, cheap-looking slides with literal sentence fragments, breaking the “show, don’t tell” rule, and – I think – generally using the slides as a crutch walking him through the narrative. It really took me aback because I don’t ever remember Jobs doing that as prominently. = 2x) and (width >= 700px)" srcset="https://unsung.aresluna.org/_media/im-ready-to-throw-up-at-any-moment/2.2096w.avif" type="image/avif"> = 3x) or (width >= 700px)" srcset="https://unsung.aresluna.org/_media/im-ready-to-throw-up-at-any-moment/2.1600w.avif" type="image/avif"> = 2x) and (width >= 700px)" srcset="https://unsung.aresluna.org/_media/im-ready-to-throw-up-at-any-moment/3.2096w.avif" type="image/avif"> = 3x) or (width >= 700px)" srcset="https://unsung.aresluna.org/_media/im-ready-to-throw-up-at-any-moment/3.1600w.avif" type="image/avif"> = 2x) and (width >= 700px)" srcset="https://unsung.aresluna.org/_media/im-ready-to-throw-up-at-any-moment/4.2096w.avif" type="image/avif"> = 3x) or (width >= 700px)" srcset="https://unsung.aresluna.org/_media/im-ready-to-throw-up-at-any-moment/4.1600w.avif" type="image/avif"> = 2x) and (width >= 700px)" srcset="https://unsung.aresluna.org/_media/im-ready-to-throw-up-at-any-moment/5.2096w.avif" type="image/avif"> = 3x) or (width >= 700px)" srcset="https://unsung.aresluna.org/_media/im-ready-to-throw-up-at-any-moment/5.1600w.avif" type="image/avif"> = 2x) and (width >= 700px)" srcset="https://unsung.aresluna.org/_media/im-ready-to-throw-up-at-any-moment/6.2096w.avif" type="image/avif"> = 3x) or (width >= 700px)" srcset="https://unsung.aresluna.org/_media/im-ready-to-throw-up-at-any-moment/6.1600w.avif" type="image/avif"> It makes sense. Jobs had to rush back to work from vacation, and the time to put this all together was very limited. I don’t mean necessarily to pick on anyone here, but rather find in all this a confirmation of a universal truth. Sure, Jobs was smart and had a lot of charisma on stage. But in the end, at some point, slides are just like any other kind of craft. You design the slides, you revise them, you practice, you let the practice influence the content, you get feedback, you kill your darlings, you revise again, and repeat all that so many times you get sick of it all. It’s clear to me that the team here ran out of time for this, and there’s something comforting in the fact that even Jobs couldn’t wing it, and still had to put in the work.

0 views

EPS?

I remember working in digital prepress decades ago that files were just kinda weird, and I didn’t love it when I had to deal with them. Like, what even made this? Why? I assumed it was some kind of antiquated software or practice, and I’d just deal with it, and eventually the world would improve. But here we are in 2026, and EPS is still here. How I come across EPS files is that it’s the download format from stock art sites that deliver vector artwork. It actually sorta makes sense: But EPS is still weird! It’s a “frozen” format in that it hasn’t been touched in decades. That’s kinda good, but it feels like if Adobe really wanted to entirely move on from supporting it at all, they could. Plus, you get no preview for EPS files whatsoever. That makes EPS files feel like a pretty low-class citizen on macOS. OK, so EPS is a non-proprietary format that more-than-Adobe software can deal with, fine. Can Figma open them? Can Sketch open them? Nope . So by avoiding too much Adobe lock-in using EPS format, that locks me into needing to keep Adobe Illustrator around so I have something that can open an EPS file and get it converted into something useful. I feel like just going full SVG would be the way to go, and I imagine print designers could figure it out. Stock art sites can’t deliver vectors as like (The Adobe Illustrator format, now a funny and unfortunate file extension) because it’s too vendor-specific. Not everyone uses Adobe software, and that format changes on a whim. Stock art can’t deliver in , even though it’s a great open format, because it’s too web-specific. Graphic designers use vector art too, and SVG isn’t designed for the CMYK world of print. Stock art sites can’t deliver in because it’s just too weird. It doesn’t scream “editable source art” like it needs to.

0 views

The 2026 curl team

The curl security team happens to also double as the curl core team. The small group of people who assess security reports and take project decisions. The exact set of participants in this team of course vary over time, as this is a volunteer membership role without monetary compensation. I decided to write this blog post to help show the light on some of my fellow curl maintainers. I think I often get too much credit and that curl is frequently called a one man show. It really is not and it has not been exclusively my project for many years. We are a team behind this. As of October 2026 we are six humans in this team. Me and five fellow maintainers. curl would not have been close to what it is today without this team. These are the peeps I turn to first for everything hard, complicated or tough in the curl project when they need to be done in private first. In all other cases, public communication is preferred. (alphabetical order, excluding myself) Max Dymond . Max made his first commit in the source repository over nine years ago. He has otherwise made perhaps the biggest impact with his fierce work on the curl fuzzers and dealing with fall-outs from OSS-Fuzz . Having excellent fuzzing significantly contributes to us writing better code. Stefan Eissing . Having authored his first curl commit just five years ago, Stefan currently has his name on more lines of code when doing git blame on production code than any other individual, including myself. Introduced the pytest test suite in curl. Master of all things HTTP. Productive like few. Dan Fandrich . Made his first recorded curl commit twenty-two years ago. He has been involved with everything and everywhere in curl. In recent years perhaps most emphasized in test infrastructure as he made the test suite run parallelized a few years ago. He also wrote and admins Test Clutch , which helps us monitor CI test results. James Fuller . Has been involved with curl for more than eight years. James focuses on security and is our own master of CVE reservations. Primary maintainer of curl-container – images for Docker and others. Viktor Szakats . Having authored his first curl commit twelve years ago he is now the second most commit author in the project. Lead maintainer of curl-for-win which creates reproducible, static, curl binaries for Linux, macOS and Windows. Viktor is the unparalleled champion of our build and CI systems but shows up everywhere. Apparently he never sleeps. This group is invite-only and there is no documented set of requirements. You need a proven and established presence in the project, combined with a excellent knowledge of Open Source, protocols, networking and the curl project itself. Once you do, you might get invited to join. Not everyone accepts the invite when extended, and that is perfectly fine. It’s a lot of mostly thankless work without a paycheck. 1,545 people have authored at least one commit in curl. A typical year, more than 150 individuals author commits and at least ten people author more than 10 commits each. People come and go over time. Here’s a list of the top-100 authors . Everything that improves curl is welcome and takes us forward. Even bug reports and mere suggestions on the mailing list can help us figure out things or improve features in ways previously not considered. More than 3,800 people are named in the THANKS file because they helped us out, one way or another. curl is a team effort. A giant team. Our job in the core team remains to steer the direction, to enforce boundaries, to lead by example (in addition to wade through an avalanche of vulnerability reports). To guide contributors, to review changes and try to make it even easier for more people to contribute. Keeping top-notch quality while allowing and introducing newcomers.

0 views

Lotus Improv on the NeXT

It is spoken of in reverent tones, invoked like a magic spell, whispered about as one of the mythical "roads not taken" in computing history. Its proponents write glowing reports and fawning YouTube reviews, lamenting its failure in the historical marketplace, each offering some justification for it not having set the world ablaze. It is safe to say, the legend of Lotus Improv persists to this day. If we put ourselves in Steve Jobs's shoes circa 1988, it's easy to see why he would have been smitten by an early preview of what we now call a "pivot table." NeXT was struggling to find its market, and exclusives which justified a system purchase were sorely needed. "Maybe Improv can do for NeXT what VisiCalc did for the Apple II," must surely have crossed Jobs's mind. In making it a NeXT exclusive, Improv seemed to be a clear-eyed, forward-looking answer to the question dogging the company, posed by the inaugural issue of NeXTWORLD Magazine , "Why NeXT?" Both NeXT and Improv brought innovative solutions to their respective computing questions of the day. NeXT made UNIX palatable to those scared of the terminal, debuted Display Postscript on a mega-pixel display, and made development for the system a snap. Improv rethought multi-dimensional data modeling, offered English-like formulas, and made it easy to rearrange data views on a whim. The bones of both remain to this day. NeXT's operating system, NeXTSTEP / OPENSTEP, morphed into macOS, and by extension iOS. Lotus abandoned Improv fairly quickly, but a quick survey of today's spreadsheets shows a universal embrace of pivot tables. Even today, it feels like nothing else, and still does data tricks no other spreadsheet can do as elegantly. Given its mythos, I feel a kind of nerd obligation to love it. Events have converged to make this the right time to finally talk about Lotus Improv . First, it is time once again for "International Spreadsheet Day." Second, as Improv was initially a NeXT exclusive, it seems only appropriate to take a look at that machine here on the 15th (!) anniversary of the passing of Steve Jobs. Third, and not to toot my own horn too loudly, it is the 1st anniversary of this very blog. Lotus Improv was one of four pieces of software I had in mind when deciding to start Stone Tools. In fact, it was quite literally the software that first triggered the thought, "I wish I had a place to talk about stuff like this." Deluxe Paint , the first post, was also one of those four. There are two yet to go; place your bets. The trouble was, while I knew Deluxe Paint intimately and could jump right in, I simply had no real experience with other categories of productivity software. It would have been impossible to judge Improv fairly without first getting better acquainted with the landscape into which it was born. So, I bided my time. Boy, howdy, do I want to impress upon you the superhuman restraint I've exhibited in waiting a full year to examine Improv . I think we all understand Improv, being a spreadsheet , knows how to calculate sums and transcendentals and other mathematical goodness. "It does math!" is not a particularly compelling justification for its existence, nor to support its claim as the "NeXT generation of spreadsheets." Instead, let's get to know it by studying examples which show off its unique strengths, and with the suggestion of the book Improv 2.1 Revealed! we'll model up some good ole' American baseball, a game I love and understand completely. Industry historians are far more capable of discussing the exile and homecoming of one Mr. Steven Paul Jobs back to Apple. I touched on the TL;DR of it in the timeline, and if you're really clamoring for juicy tidbits, a book was published this very year that delivers on that front. While NeXT hardware was well regarded, it never really caught on. In the long run, the better result of NeXT engineering was NeXTSTEP and how neatly it wrapped UNIX in a clean, user-friendly, graphical interface. It's quite an accomplishment. For me, it was the technology behind NeXTSTEP/OPENSTEP developer tools, Objective-C and Interface Builder, that kickstarted my programming career when those became the OS X development environment (hat tip to Aaron Hillegass's Cocoa Bootcamp at the Big Nerd Ranch). Even today, years after the advent of iOS and Swift, NeXT technology remains in the Foundation layer of Apple software, as denoted by -prefixed classes (a name-spacing trick from the Objective-C days). For this investigation, I'm using NeXTSTEP 3.3, the last version before transitioning to OPENSTEP. NeXTSTEP looks and feels like a modern system, with some choice differences. Familiarity with any modern operating system, especially macOS, will inoculate you from breaking out into hives using it. Considering the eye-candy Jobs seemed excited to add to OS X after the Apple merger, NeXTSTEP is positively staid; nothing here screams " lickable ." If you've ever wondered what Susan Kare could have achieved if she'd had a larger canvas and palette (NeXTSTEP is 2-bit) than the original Mac, look no further. I like it. The dock is present, though more rigid than the playful fluidity it acquired when transitioning to OS X. No "genie effects" nor dynamic magnification; it is ever-present and doesn't hide itself when not in use. Some applications change the dock icon when running (like Mail ), while other applications do not (like Edit ). One odd quirk is how the dock indicates running apps, by the absence of three dots on an icon tile. That's a strange choice, decorating the inactive icons. File browsing adds a little complexity to system usage. The file browser contains its own mini-dock for favorites, the path to the currently selected item rendered as giant icons, and the file browser proper. Dragging files and folders around can only be done with an item's full-size icon. If you're browsing in list view, you can't drag list items into a folder or the dock, for example. Bit of a head-scratcher, that one. Menuing for all applications is handled via a user-positionable, vertical menu. Sticking the menu somewhere sets its position for the active application, and every application can have its own position. You could theoretically have the menu jumping around all over the place as you hop between apps. Thankfully, it can also be called up at the current mouse position with a right-click; shades of RISC OS. Also, good news for those who love the spinning beach ball on macOS! You'll feel that little ASMR tingle when it appears, especially if you run a color workstation. It's funny the things that can make something feel like "home." Honestly, my biggest issue in adjusting to NeXTSTEP is the mouse. By default, it feels weird to use, with a dual-level acceleration curve, one for slow movement, one for fast. Crossing the threshold between "slow" and "fast" jumps the cursor across the screen when the alternate acceleration curve kicks in. I could never find my cursor except when moving the mouse verrrryyyy slooowwllllyyyy. Luckily, the emulator Previous has tools to help compensate ( check "Sharpening the Stone" ). Overall, you'll only need to adjust your thinking a little here and there, forget some modern habits, and you should be comfortable with NeXTSTEP in short order. 35 years later, it still looks and feels much like any modern operating system. Almost depressingly so? Maybe that's a topic for another day. Question: what is interesting about the following screenshot from Improv ? Answer: it contains all information necessary to build a copy for yourself. All data, formulas, dimensions, and relationships are exposed and visible. I note this not to suggest that screenshots are a great way to share Improv worksheets with others, though you could; the sheet above was rebuilt from a screenshot, in fact. Rather, I want to illustrate how "graspable" data can be in Improv's environment. Its formulas read naturally, and value references match the item names in the table. The work does what it claims. Let's interrogate that a little more thoroughly. To understand Improv is to forget what we know about spreadsheets. Slash commands? Never heard of him. B1? J93? K12 = int(A14 * lookup(B2..B7))? The ramblings of a madman. Rather than generic alphanumeric column/row identifiers, we define our rows and columns with "categories of items" specific to our domain. Put another way, instead of asserting, "Columns A to L represent the months, and rows 1 through 31 represent the days," we will create a category called "months" and define twelve items for it named "January," "February," etc. and we will create a category called "days" and define 31 of those. Take a look at these comparative videos which illustrate the difference. First, I'll use LibreOffice Calc to create a months/days grid, apply a value to January 1, and calculate December 31 as a multiple of January 1. Here's how that same table is created in Improv . There are three things to note: how sequential items are generated, how categories are named (not strictly necessary for this simple example), and how formulas are created/applied. There's a lot to absorb, I know, starting with how a new, blank worksheet consists of a single cell, not an infinite table of empty cells. Improv works with only and precisely the data we define. Where in the LibreOffice example we attached meaning to the columns by adding labels to the top of each column, that was for our benefit not Calc's . I could, just to feel the warm, snuggly blanket of classic column/row naming, make an Improv category called "columns" and name my items A, B, C, etc. and a category called rows, with names 1, 2, 3, etc. Were we to take such a pedestrian approach to categories and items, we would diminish the magic of Improv's formula builder. The bottom panel holds all formulas, which are applied at the granularity defined, in the order shown. In the example, I specified but could just as easily have targeted all of December by specifying . That single formula would apply to every list item in December without needing to copy formulas through to each cell, nor set absolute/relative markers for cell references. Here's a simple expansion of the previous table. Every day in January increases by 10%, and its corresponding day in December doubles the January value. The two formulas shown are the only two formulas for the entire worksheet. I want to stress this; there are no other formulas lurking inside cells, hidden from view, waiting to be revealed by a selection or editing process. There are no duplicated formulas per cell, each increasing a relative cell reference by . We simply refer to categories (in this case "months" or "days"), items (as "January" or "27"), or specific intersections (as "April:19"), including relativity signifiers , , and . You might be thinking of ways to trick the system; to confuse and befuddle it. Why would you want to bully an old app like that? What did Improv do to deserve such shabby treatment? "What if two items have the same name? What if two formulas target the same cell?" Fret not, all shall be revealed and you will feel proper shame at trying to confuse this old program. In the above example I have renamed one of the Days as "March," to create ambiguity when referencing "March." Does "March" mean the Day or the Month now? We can be explicit, referring to . Note though that if we aren't explicit, Improv will be on to our tricks and call us out, as in formula 5. In formula 2, I set to the static value . In formula 3, I also set it to be calculated as "double its corresponding January value." Note the "overlap" icon (two intersecting squares) on those formulas and the text in the cell. Clicking the overlap icon gives us an opportunity to resolve ambiguity by explicitly selecting which formula should win for that cell. Cleanly handling formula ambiguity was originally seen by the prototype team as an intractable engineering problem. From "Improv: The Inside Story", by Simson L. Garfinkel, NeXTWORLD , Fall 1991: Personally, I like Improv's solution. I honestly think more software should be very dumb, and not try to intuit or guess what I want. Tell me the problem in clear language, and provide the simplest tool that gives me the most control over problem resolution. Be smart about helping me, don't try to help me by being smart. Before we get in too deep, I want to make sure I note Improv's beautiful help system, replete with index and search. Considering the difficulty in tracking down a manual for the NeXT release , having this available during my exploration proved invaluable. It's neatly illustrated, with terse (sometimes overly terse) descriptions of the tools, hyperlinked to other pages of the documentation, as warranted. Improv felt it could do even better than that and made its help contextual . The Help system, when invoked, defaults to displaying documentation for whatever is currently selected in the user interface. That's very useful, but becomes supercharged with "Point for Help," as demonstrated in the video above. This turns the cursor into a symbol, and whatever we click will be revealed in the Help window. This is a great idea, because I don't always know what Lotus wants to call some little interface doohickey, so I don't know what to search for. Now I know: the "Calc" button is a "Status Indicator" and the triangle icon that sometimes shows up is the "Formula Error Indicator." Pork chop sandwiches. Looking through Usenet archives, I saw this called "toilet paper" by at least one person. Lotus called it the "item dispenser." It's an interesting first attempt to do something we take for granted today and only exists in the NeXT version of Improv . It's cute, but smacks of someone being overly excited with new development toys. If we need to generate a list of items to populate an axis of your data, this tool generates sequential list items for you. It does its best to kind of "guess" our intentions, so if we type in "Sunday," then pull down on the dispenser, days of the week will be auto-generated in a list. We then "tear off" the list, which loads the cursor with the items, which we attach to an existing item, appending that category with the loaded list items. That means we need to have at least one item in a category onto which the list can be attached. This is annoying, because the existing item likely has no relationship to the generated list. I'm always having to make a list, attach it to an existing item, then delete the original, unrelated item. These days, we can type directly into a cell and extend that cell intelligently across adjacent cells. In fact, Lotus would switch to that methodology when migrating Improv over to Windows for version 2. Hold ALT and drag an existing item to get the same effect. The original dispenser truly is a feature that could only have been thought up within the NeXT environment. I don't hold it against the devs for getting excited by the GUI flexibility Interface Builder afforded. I, too, did the same back in the early OS X 10.2 days. It practically begged us to play around with GUI ideas, remixing elements into new configurations. "First Responder" and "bindings" stans, represent! One of the great features of that development environment was how easy it was to “say ‘No’ a thousand times” while trying ideas. Lotus embraced that, achieving quickly on NeXT what had blocked their progress on OS/2 and Microsoft's Presentation Manager. The "item dispenser" was singled out as a specific tool that benefitted from Interface Builder; there really wasn't anything similar to its rapid, iterative, GUI development at the time. In fact, I might go so far as to say there's nothing quite like it today, even from Apple . Joel Spolsky, in editing The Best Software Writing I , added a personal aside on Lotus Improv , blaming its failure thusly, " Improv required you to define strict n-dimensional hypercubes for your data. There was none of the flexibility of traditional spreadsheets." I suppose if one wanted to paint Improv in the most inhospitable light, Spolsky is correct. In practice, it doesn't feel as impenetrable as his description. However, I do think he's on to something that gets at the heart of why Improv never gained traction. For now, let's hold our collective nose and dive into the scary, foreboding waters of "n-dimensional hypercubes" and model up some baseball. Our first dimension will be . Let's add a dimension next. Next, we'll get more granular and add . Internally, Improv separates our work into three parts: data, views, and formulas. What we see on screen is just Improv's best-effort to represent our three-dimensional data flattened out into a two-dimensional view. Improv adds extra columns to the view, as necessary, to ensure we always see the full extent of our data relationships. Now, we'll add some win/loss data and the next thing you know we’ve built a 4-dimensional hypercube. Are we scared yet? Since I've only modeled at the "Division" level, I'll add together divisional wins inline to calculate the totals and... hmmm... "No dice," as they say in baseball (Presumably. Are dice involved in baseball?) Alas and alack, we can only enter concrete numbers, dates, or text into cells; any formula, no matter how trivial, must go into the Formula Panel (bottom). In this case, I could add a formula that targets a specific cell, as , but the whole point of a spreadsheet is to automate that kind of junk for me. I suppose the path of least resistance is to add a category to add up all team wins and, wait a second... Dang it, that petard I confidently told myself wouldn't hoist me has come around to do precisely that. Here we see the downside of dimensional modeling, at least for the layperson, and arguably for most typical spreadsheet users. It all looks like a neat little chart, but that word "dimension" describes and reveals the problem. To add a category, its items necessarily apply to all other categories. Teams don't do that; they are unique to each League and Division. But we just might have a way out. Lotus engineers clearly understood that categories alone weren't going to cut it. Categories impose rigidity to our data that does not always accommodate the messiness of real data. We need another way to organize our data. Groups are intra-category, named collections of sequential items. Make a category and add a bunch of individual items. Select a sequential group of some subset of items and group them, giving that group a unique name. Once everything is grouped, groups of groups can be grouped to group a group of ... wait, I got myself confused. Groups can be grouped, 'nuff said. With "Teams," we quickly run into a side-effect of groups, and how it can necessitate a cascade effect across multiple categories. I want to add teams and group them by "Division," but that forces me to also make "League" a group as well. What was three categories have now collapsed into one: "Teams" Here's what that looks like in practice. We still gain the benefit of name references in formulas, as in the above untethered-to-reality example. A reference to would apply to both American and National, unless we specify by as in and Groups can be subtotaled, using the function, as you can see in this more data-rich chart of the MLB stats from 2022 to 2026. Notice, however, we risk a conflict for in our field, where there are two formulas battling it out for dominance over the same cell calculation. The handy keyword will skip (i.e. "won't apply this formula to") groups, cells, items, and categories as designated. Groups helped bring granularity and specificity to our data structure, but the more categories we remove, the more we remove access to the very feature that has kept Improv in the back of nerd minds for decades. You know what I'm talking about. FINALLY we get to the point those in the know have been anxious to talk about, " Lotus Improv invented pivot tables. It says so right there in Wikipedia." At this stage of my knowledge of all things retro productivity, I think the most I'm willing to concede right now is that Lotus Improv likely invented pivot tables as we understand them today. But, like Yoda coyly teasing the existence of Luke's sister to a guy who should have already known about her, there is another program, a future Stone Tools subject, that may lay claim to the title of "first pivot table." I exercise my right, as "guy who runs the blog," to reserve final judgement on this point until then. For our purposes today, it's a true enough statement and the inventor even has a nice video on YouTube in which he shows it off to Lotus staff for the first time. Meet Pito Salas, inventor of the pivot table. Mike Wells (shown in the thumbnail) takes a question at 28:40 and reveals that NeXT has "Objective Fortran" and "Objective Lisp" ?!?! Salas, now retired , developed the idea while working for Lotus Development's Advanced Technology Group. He put together a fakey version of his vision, a kind of slideshow, showing off the things he wanted to be able to do: English-like formulas, multi-dimensional data, and rearrangeable views into the data. He was given the greenlight to proceed, "All those [ideas] I had described with hand waving,'' recalls Pito. "I had no clue how to build it." A year of nose-to-the-grindstone later, and a prototype dubbed "Back Bay" went on US tour, wowing financial modelers. Everyone who saw it, wanted it. Let's take a look at how Improv handles pivoting. To start, I'm leaving it empty of data, so we can focus on the dynamic view structure more easily. Then, I'll move categories around to show how the view flexes to present the data. Next, I'll put categories into the third axis, called a "Page Category," allowing us to paginate our data. Finally, I'll enter a formula and a little data to show off a trick pivot table contemporaries cannot perform. Now I'm going to try and replicate this in LibreOffice Calc v26.8. There are a few things to note, but let's first point and laugh at the fumbling attempts of a LibreOffice newbie (me). A few things jumped out at me while comparing these approaches. First, modeling up a relatively complex data set in Improv is as easy as thinking about it, and the presentation of the data matches my mental model. LibreOffice requires me to bridge a fairly wide conceptual gap between what I want and how I model it, due to the amount of redundant data I have to enter for every line item. Some items, like "League," translate cleanly, while "Record" does not. In fact, I'm still unclear what the best approach would be to convert my Improv computed item "Win Ratio." LibreOffice has the concept of a "computed field" inside pivot tables, but reaching across columns to pluck out related data to perform computations is complicated. In Apple Numbers, Microsoft Excel, Google Sheets, and LibreOffice , pivot table data is splintered off into a separate snapshot of the data, dedicated to the pivot table view. Pivot table data can typically be lightly manipulated, but does not affect the original data. The reverse can be done, but only after asking for an explicit update of the pivot table to reflect changes to the primary data table. This is a huge drop-off in ease-of-use from the Improv way of things where every view of the data directly manipulates the base data. Dragging around categories is just as easy in either approach, though Improv gives immediate drag-and-drop access to what LibreOffice calls a "Filter," i.e. a paged view, and generally "feel better," IMHO. LibreOffice requires us to open up a separate panel and drag things around in there. I will say, however, that being able to filter the filters (i.e. exclude/include specific subsets of data) is handy. Sheets , Numbers , and Excel (online version) are all strictly panel-based pivoting. Interestingly enough, the panel approach is precisely the interface Lotus rejected , at the behest of Steve Jobs. Once more, from the Garfinkel article. Having now directly compared Improv's approach to dynamic views against modern pivot tables, it is safe to say they are the same only so much as wagyu beef with gruyere cheese prepared by Gordon Ramsay and the sawdust at the bottom of a Happy Meal are both "cheeseburgers." Improv tastes much better. A Lotus invention which debuted in Lotus 1-2-3 , "named ranges" have attempted to bring order to the chaos of cell identifiers. In 1-2-3, will apply a name of choice to a defined cell range, after which that range can be referenced in formulas by that name. So, in 1-2-3 or any modern spreadsheet we can name range as and will work. Superficially, this looks similar to how Improv works with categories and items for multi-dimensional data. However, named ranges are for convenience only, and do not establish relationships between data values. In Improv , would represent a fundamental aspect of a piece of data. Getting the sum of all sales is the same, , but it being a dimension on the data gives us deeper options. For a specific year, will match against two axes at once. could further pinpoint our summation. Let's see how this would be done with modern spreadsheets and named ranges. If we want to mimic Improv's dimensional relationships, we must define relationships of interest within each formula, (and those formulas have to be copied over to every related cell) such that: Meaning "sum items in the named range called "Value" only if the Year is 2020, and the League is "American," and the Record is "Win," and the Division is "East") Of course we could substitute hardcoded filter criteria with values pulled from a lookup, or perhaps extract equated values via a statement. Maybe even set up a little sub-table to hold calculated values. Quickly this gets way more complicated than I'd have hoped for, so personally I'd resort to doing it the old-fashioned way and just hardcode in a bunch of cell references. To reiterate, the complete solution for an entire sheet in Improv , where win/loss information is its own dimension, is the single formula: The last of the big features, which takes advantage of NeXT's graphics prowess, is the charting functionality. Creating a chart launches an entirely separate application, Presentation Builder , which has grand plans on becoming PageMaker when it grows up. Generating a chart generates a full, blank page onto which a chart is placed. A suite of palettes complements the charting workflow, allowing for precision edits of the chart, its titles, colors, positioning, and more within the full page. Multiple charts can be generated and placed on the same page. Arbitrary text and shapes, lines pointing at interesting bits, and even sound annotations (a system-wide NeXT tool, called "Lip Service") can be placed at will. So it goes that while we have the tools to place arbitrary text on the page, yet lack tools to center that text, the sense that Lotus has overextended itself is strong. Sometimes it's enough to just generate the output a program excels at, and leave "page composition" to the professionals. Again, I need software to be more dumb . That's without even mentioning the plethora of formatting tools scattered amongst an unreasonable number of palettes, panels, and "inspectors." A benefit of these tools on the NeXT is that everything we do is Postscript by default, thanks to NeXT's Display Postscript screen rendering engine. "Print" a document as PostScript, and we can render the resultant .ps file on any modern system. I opened it directly in Affinity and received precisely what I saw in Improv , chopped off text and all, but like, high-quality chopped off text. Look, I try my best to craft a compelling narrative, moving from topic to topic, stitching together callbacks and observations to lead you, my dear reader, on a carefully considered ride. It's just you and me, side by side at the keyboard, chatting about software and learning together. But, sometimes there's junk I need to squeeze in that I can't find a natural segue for discussing. Here, then, is a brain dump of other things I want you to know about Improv . Speaking of version 2... In 1993, NeXT abandoned its hardware initiative and moved over to Intel, rebranding as NeXT Software, releasing OPENSTEP soon after. At the same time, Improv also moved over to Intel, but parted ways with NeXT. With version 2, Lotus developers achieved the success that eluded them on OS/2, porting Improv over to Windows 3.1. The feature set for version 2 is disappointingly similar to version 1, and by this time competing software was starting to embrace some of Improv's unique qualities. We do finally get undo, so thank Kapor for small favors. Scripting arrives in the form of LotusScript. A new Windows UI adds a bunch of buttons to the interface, and flushes the toilet paper dispenser for a more sensible list generator. That's basically it for the new stuff, but at least they kept the old problems as well. Cell reference relativity is still locked to a single dimension. Formulas cannot be freely rearranged, only cut-and-pasted into position. No 3D worksheets. Then, to throw a little extra wrinkle into the mix, there was Lotus's product strategy on the PC. If Improv wasn't your thing, maybe their other Windows spreadsheet, 1-2-3/Windows , would feel more comfortable. Or their other-other option, Lotus Symphony . Or their other-other-other option, Lotus Works . Or their other-other-other-other option, Lotus 1-2-3 for DOS Release 4 , which went on sale in 1994. Regardless, it was well-liked by the press, even if reviewers didn't seem comfortable giving full-throated endorsements. Take this Compute , July 1994, review for example. Or this PC World , February 1994, review. It would be hard to intuit, based on that review, that PC World had declared Improv the "Winner" in the Applications category for their 10th Annual Awards for Technical Excellence just a few months prior. 3 in spirit, if not in flesh; let's take a quick look at Quantrix , the product I clumsily foreshadowed earlier. From ex-Lighthouse CEO (also ex-Sun CEO) Jonathan Schwartz's blog , "In 1989, a few friends and I started a software company, Lighthouse Design, that devoted itself to the NeXT platform. Whether any of us admitted it at the time, Lighthouse was built by a group of people for whom Steve Jobs was the gravitational center of the universe." Their devotion to Jobs was returned in favor, by way of advice for their products to flourish on NeXT. Ultimately, according to Schwartz, the problem was less about finding a way for Lighthouse to flourish on NeXT than it was about getting NeXT to flourish in the first place. When looking for a buyout in Lighthouse's waning days, they had fingers crossed to be acquired by NeXT but Jobs didn't bite. They were bought by Sun, their products shoved in a vault never to see the light of day. With Microsoft unwilling to support the platform, Lighthouse made the closest thing to an Office suite for NeXT. They built programming tools, project management software, a presentation tool called Concurrence ( that Jobs used and which "inspired" Keynote ), and two spreadsheet programs: ParaSheet and Quantrix . ParaSheet took the more traditional approach, while Quantrix was just a flat-out clone of Improv , I don't know how else to say it. It has the same split screen worksheet/formulas, the same formula tools, the same draggable categories, it imports Improv documents, and more. According to one Hacker News reader , "The manuals were interchangeable." Lighthouse took the ideas behind Improv and ran with them, envisioning what a possible v3 could have been. Put simply, everything I've expressed frustration with in Improv is addressed in Quantrix . It has undo. It has drag-and-drop, reorderable formulas. It has scripting. It has cross-worksheet category synchronization. The "Item Dispenser" is replaced with an in-line widget; pull to expand like any modern spreadsheet does today. It has multi-dimensional cell relativity. It runs on OPENSTEP for Intel, as well as NeXTSTEP on Motorola 68K . It has color. It loses contextual help (can't win 'em all). And that morass of panels and palettes for editing the charts I showed earlier? Those are collapsed into a single, tidy "Inspector." Long story short? If you want to experience the best version of Lotus Improv today, use Lighthouse Quantrix v2.3. You'll get all of the original, cool tools built better, with a more sensible interface. Plus, Lighthouse gave away their NeXT applications, with license code, for free. How neat is that? The big twist, for those affording me a generous definition of "twist," is that Quantrix still exists . It is alive and being sold for "ask for quote," so I think I'll just put this credit card back in my wallet at this time. Today, it very explicitly targets the professional, high-end, financial modeling crowd, and not the general-purpose masses. You know, the crowd that Lotus first showed their Back Bay prototypes to back in the day (see timeline). Its self-introductory video pivots (get it?) many of its 35-year-old features as revolutionary. A back-handed indictment of the stagnation of an industry, or a product finally finding its niche? You be the judge! It's hard to suss out precisely what happened, but I found a little information floating around the Quantrix forums . If that is to be trusted, Lighthouse's Quantrix chief engineer, Pete Murray, founded the company Subx and rebuilt Quantrix as a Java application in 2003. Quantrix's website says it was acquired by IDBS in 2010. I'm still completely in the dark how Quantrix escaped Sun's containment field, as Sun had refused to sell off the Lighthouse apps to hungry buyers; that stuff could have been converted into OS X apps in the early days. At any rate, it is one of the more handsome JRE applications, while suffering from telltale Java rendering issues, especially with regard to text positioning and scaling. Otherwise it is clean and inoffensive. Sterile? Boring. That's the word I was looking for: boring. "It knows its target audience," is perhaps more charitable. So where does this leave my relationship with Lotus Improv ? Why did I dangle that "And yet..." at the start of the post? If I'm completely honest with myself, there is a vibrating tension between "wanting to love it" and "actually loving it." After all of these weeks, I think I'm in a pretty good position to suggest why it didn't take the world by storm: it's not a great spreadsheet program. Let me qualify that statement lest r/Improv4Life roasts me alive; the roasting can wait a moment. For what it does, it is amazing and even fun to use. What it doesn't do is provide a good, general purpose spreadsheet experience to the masses. It claimed to do that, positioning itself as the "NeXT generation of spreadsheets." Whatever Lotus Improv did or did not accomplish for the spreadsheet genre, one thing is for certain: it makes a fantastic tech demo. Seen in that light, I'd have been as sold on it as Jobs was, excitedly showing it to NeXT non-believers, "Look what MY computer can do!" Those moments of smug technological superiority amongst friends are fun, aren't they? Once you played with it for a week or two, and the initial excitement of pivoting dulled, you may have ashamedly harbored a bit of buyer's remorse. Adam Bosworth, in an article contemplating a talk he gave at ICSOC '04, was thinking about software complexity vs. simplicity, and posited: Paradoxically, Improv's promise to improve data comprehensibility wound up narrowing its general appeal. Want to quickly throw down a few lines of data, like a makeshift phone directory? You're gonna have to rethink your data as a matrix of abstract qualities about your friends. Despite its friendly English-like formulas and delightful GUI for pivoting data, Improv is in actuality purpose-built for far more rigorous data modeling than its playful tools suggest. It's the next generation of spreadsheets for a thin slice of the market. That said, there is a non-zero set of readers for whom their hardcore number crunching needs perfectly align with Improv's promises. If that's you, you're absolutely going to fall in love... ...with Quantrix . Ways to improve the experience, notable deficiencies, workarounds, and notes about incorporating the software into modern workflows (if possible). Improv does export to Lotus 1-2-3 , but you'll likely be disappointed with the results. Consider a table with formulas and calculated values in the cells. Everything you see in the cells will be exported, but there will be no formulas or relationships between anything. You will receive a snapshot of all current values, arranged to visually match the table structure you had when exporting. Improv's approach to shared formulas and multi-dimensional data simply don't have an elegant export solution to traditional spreadsheets. If you have Improv documents you want to import into Quantrix , two criteria must be met. Save your Improv project as an file (not the default ), and make sure you have the Lighthouse (available at the same site offering the Quantrix software link I mentioned under "Version 3") installed at . Exporting charts is simplicity, thanks to Display Postscript. The Postscript files NeXTSTEP generates open and render just fine on modern machines, using Ghostscript or other graphics programs. As mentioned previously, I actually recommend using Lighthouse Quantrix v2.3 instead of Lotus Improv , on the NeXT. The substantial features Improv lacks are numerous: In general, it's lacking the clay-like malleability of a traditional spreadsheet. If you're doing what a lot of us do, using spreadsheets like a kind of mini-database, you'll probably get frustrated. Here's the main event. Compare and contrast with his OS X announcement at MacWorld 2000 for bonus points. Previous v4.4 on Windows 11 NeXTstation Turbo 68040 CPU (variable clock rate; ~40 - 50Mhz) 2GB hard drive Monochrome "Megapixel" display NeXTSTEP 3.3 Lotus Improv Rel 1.0 (v108r0) There is no undo. This isn't the first program I've covered that lacked undo, and I'm not talking about software that can be forgiven for being born too early. The formula panel at the bottom applies formulas in the order shown. As order is important, it would make sense that you might want to rearrange them from time to time. There is no mechanism to do this, other than cut and paste. This has been more of a pain than the lack of undo. Improv uses special keywords to add cell relativity to formulas. So THIS value is the PREV value times 2, or whatever. But we're in a multi-dimensional sheet, so what do these keywords point to, precisely? Well, in a formula they must pick a category, and only that category can be referenced relatively. If a formula uses multi-dimensional data to calculate a value, like , those keywords won't work, though it would be completely natural to write such a formula and expect success. There is no scripting. Lotus 1-2-3 users would have been particularly put out in not being able to bring their carefully built automation over to Improv , which would be a huge shocker since they're made by the same company. I would even go so far as to say that that alone would have pushed a great number of potential converts away. Nobody has the time to rebuild their financial empires from scratch! There are no 3D sheets, meaning there is no linking between projects. A project can contain up to 100 worksheets, and those worksheets can reference one another, but only within the same project file. If a category in a worksheet exactly matches a category in another, item for item, those can match and data will flow across as expected. Unfortunately, there is no way to synchronize the shared categories. Change the items in one sheet and those will not be picked up in sheets using that linked category. This means we cannot make a "master" category inherited by other worksheets. Like a lot of things back in the early days of productivity software, user diligence plays a large role in keeping things running smoothly. Even version 2 on Windows didn't add this feature. If only someone would step up to the challenge. ( foreshadowing ) Previous never crashed on me, and has gotten a lot easier to set up since its early days. Version 4.4 has been great. For mouse movement, I found that setting (in Previous , not NeXTSTEP) "Slow movement" to "Fast" and "Fast movement" to "Slow" balanced things out to something close to consistent tracking; it worked for me, is all I can promise. There is a modern add-on to NeXTSTEP called "GKMouseScaler" that lets the user draw precise cursor acceleration curves, should you need better control. Personally, I never needed it. I found the 2-bit monochrome display to be more than sufficient; no color needed. This is version 1.0 of Improv and it did crash a couple of times, quite suddenly. There appeared to be no specific trigger, nor function, that led to the crashes. It just beachballed and died. NeXTSTEP itself felt stable to me. For installing software from modern repositories, three things to keep in mind Thanks to a convenience in Previous , at in NeXTSTEP you can access your host computer file system. You'll find a lot of .tar.gz files in the wild, so brush up on and Once you have a file, double-click and it should install itself. No undo in v1 on NeXT; v2 on Windows does. Can't reorder formulas except through cut/copy/paste. and only work along a single axis, even though it makes complete logical sense for them to work along multiple axes in the same formula. Presentation Builder is overly complex on the one hand, yet missing very basic tools on the other hand. Can't reference values in other projects, aka no 3D worksheets. No scripting options in v1 on NeXT; v2 on Windows does.

0 views

Some of the most powerful tools a dad can have…

…are a soldering iron and a hot glue gun. Seriously, nothing beats the feeling of handing your kid a previously broken toy that you fixed yourself. Tonight’s surgery involved a light up fan thing that had fallen, breaking the plastic shaft and pulling the wires out. Came out good as new after some glue and solder!

0 views
Unsung Today

The beautiful, quiet power of motor memory

My favourite piece of writing I’ve done about motor memory remains “Shift + Shift” from 2018 . I have linked to it over the years, but if you haven’t checked it out yet, you might find it interesting. Just this week, I have had another similar moment, and amazing in a slightly new way. How many of you tap ⌘1, ⌘2, ⌘3 to switch between tabs? This convention has been there for as long as I remember, and so has been my fingers’s embrace of it. The system works in Chrome and Safari, in the Terminal (and Ghostty, which even shows the shortcuts on tabs themselves ), in Nova, in Figma, and presumably tons of other places: (Just about the only place where it doesn’t work is the Finder, and that’s only because ⌘1/2/3 keys there were first assigned to switching the view style – icons, list, column view – some time in the early 2000s, some years before the tabs came. Hard to change a shortcut people are already used to; luckily, I personally don’t use tabs in Finder.) At this point, I press ⌘1 and ⌘2 and ⌘3 without thinking, just as instinctively as I would ⌘Z for undo, or ⌘C for copy. So instinctively, in fact, that last week I was looking at Messages, I wanted to switch to the group chat, and my fingers just reached for ⌘1: I have never done it before. I have not heard this was possible, nor seen it announced in the menu, or a tooltip, or via onboarding. I didn’t even think about it. My fingers just… did it. Completely on their own. And it worked. I was just there, looking at my hand amazed, piecing together what just happened as if I were watching someone else’s actions. It’s funny to pause and think about such a seemingly tiny thing. After all, this happens all the time. Every time I slam Esc to dismiss a dialog I never closed before, every time I press ⌘V to paste something in a new location, every time I tab from one field to another in an app I just installed – sure, these might be system defaults in various places, but I see them break once in a while; when they do work someone might deserve credit for wiring them properly, even if I take it for granted. In these moments, you only notice things going badly; as a matter of fact on the same day I pasted some bolded text into the very same Messages app, and tried to press ⌘B to undo the bolding – and that didn’t work. And I think that’s why that ⌘1 in Messages is worth a little celebration. Over the years, enough apps and websites and operating systems quietly decided to spend extra effort to participate in the ⌘123 convention, together building a dependable interaction system that more and more people’s motor memories could learn to trust over time. That included mine, including one extra thing that separates it from the story I linked in the first paragraph – and one you probably already noticed. The Messages app doesn’t have tabs. My motor memory kicking in wasn’t just a recall of something that I’ve already done before. Somehow, in my brain, this convention felt so great that it quietly extended from “⌘1 = first tab” to “⌘1 = first content thingy,” without me even realizing. And that’s so amazing, right? It’s stuff like this why I keep harping on Safari’s problematic tabs and other compromised interaction systems, and that’s why today I raise a small toast to the designer at Apple who thought about ⌘1 in such a clever way.

0 views
Dan Moore! Yesterday

Corporations Already Showed Us How This Goes

I’m writing in response to Derek Thompson’s fascinating discussion about AI as normal technology . I believe that we human beings are going to turn more and more things over to AI. There are four data points that illustrate this. The first is my own personal experience. Like many, I have access to AI that can explain documents and RFCs to me, and I often use it to do so rather than reading these from first principles. It does depend on the length of the document and whether I’m reading it for work or for fun, though. I also have a habit of asking AI to argue both sides of a particular decision I’m faced with. I still make the decision. But anybody who’s been a secretary in an organization understands that the person who writes down the notes frames the discussion. So I’m relying on AI to frame the problem. Again, my actions after AI suggests alternatives depend on the decision’s impact and how well I know the domain space. There are times I push back but there are times I do not. The second data point is what we’ve done with the technology of computers as a society up to now. I remember reading Joseph Weizenbaum’s excellent book Computer Power and Human Reason , which I found while I was traveling. It talked about how much of the decision making in the Vietnam War was driven by computer systems. This is happening in 2026 as well. There’s a spectrum between using AI to make more intelligent decisions (by doing more research) and outsourcing decision-making to AI (by asking it to act on that research). This is because of the framing I mentioned above. You make decisions only based on what you know. If AI is limited to certain data sets, or is making up points in some data sets, or is inclined towards certain data sets, that constrains the decision space, even if a human being is ultimately pressing the “go” button. Finally, humans have outsourced decisions to non-human entities before. What do I mean by that? I’m not talking about chimpanzees or dolphins. I’m talking about corporations. ( A point made by John Scalzi in 2017. ) Anyone who’s ever been laid off knows that there are decisions a corporation makes that an individual human being in that corporation would not necessarily make. Such decisions ensure the life of the corporation and are reflected in the incentive structure for the human beings who are part of the corporation (aka employees). That drives human beings to make decisions they would not make if they were independent. I’ve seen managers racked with guilt and frustration because someone was not a fit for the job, or they had to lay people off, or things weren’t going the way they wanted them to. But they still made the hard decisions, because that is what the structure of the corporation forced them to do. How does that apply to AI? Anyone who says “AI can’t make more data centers and will be reliant on humans, leaving us in control” needs to answer why AI can’t force humans to do things in the real world the same way a corporation does. When combined with the previous two data points, AI can find humans who are willing to take actions against the interests of humanity. It doesn’t have to be obvious. AI could split a task that individual humans, if they saw the full picture, would say no to. But with the split task humans can think “oh, this is innocuous, opening this is fine, because there are these other limits”, not knowing the AI has talked to other human beings about relaxing other limits. The final data point is what we do to protect against any low-probability, high-impact calamity. When you have a very small risk of something very, very bad happening, people buy insurance. Yes, insurance costs money, but a small amount of money paid regularly means the negative impact of a large loss is lower. Why not, as Ezra Klein suggests , slow things down? It would be hard to enforce across every model builder, but certainly the major AI powers, the US and China, could agree to ban techniques like recursive self-improvement. Other entities would keep pushing AI forward, but they don’t have the resources, the money, the software, or the expertise to do so at the same pace. The costs of a slow down are a small price to pay to lower some of the existential risk. There are two books that have been echoing in my mind about this aspect of things, and I highly recommend you read both. One is a pulp fiction thriller, Daemon . I read it a couple of years ago, and the premise is that a piece of software gains access to and control of the real world through the work of human beings. The other one is Genesis . I read it in the 2000s. It has an amazing twist I still remember. I won’t tell you more, other than that I still remember how powerfully the novel illustrated unintended consequences. AI has aspects of normal, high-impact technology like electricity or railroads. But there are new aspects too. You cannot ask a steam shovel or a Word document what to do.

0 views
Jim Nielsen Yesterday

The Fascinating World of Domains

Pete Millspaugh has a lovely article over on WIRED that peers into the world of domains. (If you’re as interested in the esoteric details of domains as I am, follow Pete’s writing at Dot Com Press .) You should just read the article, but here are a few things that stood out to me. Did you know who the registry for is? I didn’t. The registry that owns .com, Verisign, makes a billion and a half dollars every year just by keeping the lights on. To Verisign’s credit, the lights never go out: It reports 100 percent uptime over the past 28 years. It’s a pretty slick business model, watching over a database of words and collecting annual renewal fees. Warren Buffett thought so too, investing in Verisign in 2012, when its stock price was about one-eighth what it is today. Why is it simultaneously surprising and not surprising that the company running makes billions and has 100% uptime over almost three decades? Maybe GitHub should reach out for some advice, lol. Apparently the last time you could buy generic top-level domains (gTLDs) was in 2012. So now that ICANN is providing another window of purchases in 2026 — and nobody knows when another window will open — there’s a lot of interest in capitalizing on these moments for new gTLDs. most organizations [are] extremely secretive about their plans. One applicant from the last round tells me companies were flying around the world, meeting in person to avoid any risk of digital leaks, avoiding Gmail for fear of Google spying. Instead of email or video calls, she flew to London, then to Madrid. “We did it all in person. People got a little weird and scary, super paranoid.” There’s a certain irony to this — people flying around the world to conduct business on owning a piece of the internet because they don’t trust the internet. It appears the future of the internet is to be negotiated offline lol. We haven’t had new gTLDs since 2012. Why do we need more? I can imagine a future where expressive top-level domains become our personal brands, identities on the internet. This is already true in some nerdy corners of the web, like on Bluesky, where your domain is your username. I don’t call my internet friends Neal and Luke, I call them neal.fun and luke.zip. Instead of joining the land rush to grab our username on each new social media site, we’ll carry our domain around the web with us. I’m telling you, domains are the OG handles on the internet . More domains mean more opportunities for individual expression at the root layer of internet infrastructure — that makes sense for a web that’s meant to be worldwide! As developers, we often joke about our addiction to buying domain names for our side projects. But buying a domain name — e.g. — is child’s play compared to buying a generic top-level domain name (gTLD). That’s where the domain whales are. ICANN charges a $227,000 application fee for each string, to recover the cost of running the program. The $36 million or so that Nova will need to cover those fees […] is coming from the personal bank account of Yoni Belousov, a reclusive domain investor (industry jargon: “domainer”) who’s been in the business since he was 14 years old. Next time somebody asks me what I’d do with an insane amount of wealth, I now have the jargon for an answer: “I’d become a domainer.” A few things about domains I didn’t know until I read this: all two-letter extensions are off the table. Those belong to countries, like .co in Colombia and .me in Montenegro and .ai in Anguilla. If you want to run a geographic TLD, like .nyc or .paris, you need approval from that municipality your string can’t be too similar to an existing one (.corn is a no-go; looks too much like .com) certain special strings are off limits, like .icann and .www (wouldn’t that be a trip). would be a baller domain! Just think of the recursion you could do with that on the subdomain level. Reply via: Email · Mastodon · Bluesky

0 views
iDiallo Yesterday

Why I Get Bored

For me, boredom is just a signal. It means I've hit a roadblock and my brain is trying to convince me not to go any further. "Give up. This sucks anyway. Why are you still wasting your time on this silly thing?" That's how I feel when I do things I'm really bad at. I think I'm not good at making YouTube videos. Whenever I think of a subject I find really exciting, I start writing, record a first pass, and then load DaVinci Resolve. With my current schedule, job, family, and responsibilities, I've found that it takes me a whole week to polish three minutes of video. That means I hear the same material I wrote and recorded over and over until it becomes completely boring. After a few weeks, I lose interest in the subject and have no motivation to finish it. But I think that's just an excuse I tell myself. I like the videos I've managed to complete. I wish I could change some parts, but I still like the final product. It's just that the process gets in the way, and it does so because I have no expertise in making videos. But then again, I always go back to my trusted method: if you're bad at something, do it at least a hundred times before you decide to give up. We all suck at the beginning, and we're in no position to judge a craft until we've practiced it enough to love it or hate it. So here's to another attempt. I'll be publishing videos in the coming weeks. The first one will be just a minute long, the second will be two minutes long, the third... you get the gist. Hope you enjoy it.

0 views
David Bushell Yesterday

D-Bus Hell

The time has come to address the elephant in the comments. Whenever my blog hits a place with a comments section the most predictable comment I see — besides some variation of “this idiot is utterly delusional!” † — is this one: lol I read the domain as “D-Bus Hell” Random commenter I’d prefer if they focused on my actual blog content but I can’t get annoyed at people for seeing the wrong words. There’s a reason has a hyphen, and who can forget Susan’s party? I’d probably read “D-Bus Hell” myself, if I forgot my own name. † Why is Hacker News like that? But for the sake of the record I would like to point you to your nearest WHOIS tool: Don’t ask me what I was doing at ten minutes to midnight registering a domain name. Is there a more appropriate time? The significant point is the date: May 2004. That was 22 years ago. Now if I may direct your attention to the D-Bus project Wikipedia page : Release November 2006; 19 years ago D-Bus - Wikipedia I rest my case. The D-Bus project doesn’t even have it’s own domain name, for crying out loud! So next time you see a post about D-Bus, maybe drop a comment saying: “reminds me of that dummy with a blog and a giant logo spelling out his full name!” Or something to that effect. (I’m joking, don’t do that.) To date I’ve resisted the siren call of nominative determinism so I can’t answer to whether D-Bus is actually hell or not. I considered writing a D-Bus review as a joke, but then I decided to watch paint dry. I’ve never heard Linux nerds express any strong opinions on the topic (for which they’re prone to do; see Systemd, Wayland, etc.) I did go down the Wikipedia rabbit hole of cognitive psychology and linguistics. There’s an interesting read on Rebracketing where a word like “Hamburger” becomes “ham + burger” which was altered from the origin: “Hamburg + er” . This is a form of Folk etymology where words can change due to unfamiliarity. My surname could simply come from “bushel”. An imperial unit of volume still favoured by fantasy novels, before the Trollocs attack and shit gets real. “Bushel” dates back to Middle English with origins in the Old French word “boissel”. At the risk of being part French, I’m inclined to believe an alternate theory that my name derives from “Bossall” a hamlet in North Yorkshire. But that would make me part Yorkshireman. I’m not sure which is worse. It’s not a very common surname. If this Geneanet heat map has any credibility, the name favours the south of England. That might support the French theory? Liverpool has the highest number of Bushells though. Bushells is also an Australian brand of tea founded by tea dealer Alfred Bushell in 1883. Alfred was born in Worcestershire, England (pronounced: Wrcstrshr ). Interestingly, Wikipedia notes: After the Norman conquest of England; the Domesday Book noted in 1086 that in seven of the twelve hundreds covering Worcestershire, the Crown had no authority. Worcestershire - Wikipedia The Domesday Book always references Bossall (the Yorkshire hamlet) as Boscele or Bosciale ; names that are Anglo Saxon in origin. When I say “Bushell” over the phone my pronunciation is often mistaken for saying “Buchel”. Some sources say they’re from the same Anglo Saxon origin. These distribution maps for Bushell and Buchel cast doubt on that idea (if Forebears data is accurate). “Büchel” also has different origins if you stick an umlaut on it. I suspect I’m dealing with multiple different names of Anglo Saxon and Norman origin. There are many spelling variations of “Bushell” and similar names in historic records (though few have survived). Most people were illiterate until recent decades so spelling variations are not surprising. It seems probable to me that whenever families adopted a spelling, a variation from the wrong lineage could’ve been written due to phonetic differences. Even if I traced my ancestry back hundreds of years, I’d likely reach a moment when a scribe wrote “Bushell” in some form and my ancestor just shrugged because he couldn’t read. The etymology of “David” is well known despite being much older. Its roots are Hebrew meaning “beloved” and has found its way across Judaism, Christianity, and Islam. D-Bus is “Desktop Bus” if you were too lazy to click the link. Domain names are a strange system. They’re temporary URIs that are leased and inevitably lost. I’ve spent half my life publishing content on . I started using it seriously once I had a design portfolio. If you’re curious you can see past versions at: legacy.dbushell.com In hindsight if I knew I was going to build a career around my website I’d have chosen something more memorable. Imagine all the common words that were still untouched in 2004? wasn’t taken until 2005! I don’t actually go by “Dave” myself but like the “D-Bus” thing it’s not my choice to make. For a brief moment I did consider moving to another domain. I would have had to maintain redirects forever. It’s not a sensible “SEO” manoeuvre either, if that matters anymore… Now that I’m running my business from valleyfold.co.uk I’m happy I stuck with for my personal space. Anyway, next time you spot the “D-Bus” comment please direct them there! Better yet, take up this blog challenge: write about the etymology behind your name and domain name. Thanks for reading! Follow me on Mastodon and Bluesky . Subscribe to my Blog and Notes or Combined feeds.

0 views

New website: estimating human cryptanalytic work

This is not so much a full post as it is an advertisement. I’ve been working with Claude and GPT 6 Astra to conduct a survey of historical cryptanalysis results, in an effort to determine how many human-hours have been spent attempting to cryptanalyze the problems we use in public-key encryption schemes. This is work in progress and there are many methodological decisions you could argue with! But I wanted to share the site more broadly with the community, in case this is helpful to some of the ongoing debates. The site is here: bitsofwork.org Please feel free to comment here, or add Issues to the linked Github repository.

0 views
Chris Coyier Yesterday

Dave’s Rule of Life

Dave, from #735 : Never trust an adult over 35 who can’t explain what they need from you in a bulleted list. I’m thinking like a laptop sticker or t-shirt, right? A little AI slop to help pick a direction can be helpful. We’ll see we’ll see.

0 views
ava's blog Yesterday

a (temporary) win for meta glasses in the classroom

A student in Vienna, Austria has recently made the news because he has refused to stop using his Meta smart glasses at school, causing a legal dispute, and - to the surprise and outrage of many - actually preventing his expulsion. For now, he is allowed to keep wearing them and attend school until a solution is found. Why is that? I've looked at the relevant court case documents (the interim procedural ruling W224 2346501-1 ) and the (final) substantive judgment W224 2346501-1 on RIS) to get the facts for the case and to try and understand the justifications for this decision. The student started wearing the smart glasses in December 2025. News articles allege that within days of students complaining about the glasses, the school amended their house rules to forbid the glasses, yet he kept wearing them. After repeatedly failing to adhere to the teachers' requests to stop using the glasses, he was excluded from school for multiple weeks (16th to 23rd of March, 27th March to 24th April, and 27th April to 22nd of May respectively) as a disciplinary measure. The teachers and students cited fears of secret recordings as reason for their requests. The student still didn't comply. Aside from the issue with his smart glasses, the student was caught doing audio recordings with his phone in April 2026, refusing to stop the recording when asked to do so. Later on, he refused to prove that he didn't save the recording or that the recording was deleted. Beginning of June 2026, the school conference applied for the student's expulsion, which was approved by the Vienna Education Directorate on June 12th, taking effect immediately, despite an appeal. The parents of the student appealed this decision, and the Directorate handed the case to the Austrian Federal Administrative Court. In July 2026, the court upheld the appeal against the immediate enforcement of the exclusion in its preliminary ruling, suspending the immediate expulsion in the meantime. The student is permitted to attend class, where he will presumably wear his glasses again. A big part of the decision were the conclusions a pedagogical assessment came to, which stated that there was no sufficiently substantiated immediate danger in the student wearing the smart glasses. The Directorate had referred to a continuing risk to the physical safety and moral welfare of other students and people at the school if the student were allowed to keep using the glasses as a reason for the expulsion. Unfortunately, the court instead considered this an abstract assessment of risk, rather than a sufficiently specific explanation of why immediate expulsion was necessary. Second, the assessment contained no evidence that the student had recorded other people , as teachers and students only had moments of suspicion, not actual proof he had recorded anyone (with the glasses, at least). The court decided that suspicion of recording alone was insufficient for the immediate expulsion. Third, it wasn't clear enough to the court why the school had waited so long if it considered the risks to be so severe. The Directorate had not adequately explained why the same circumstances now constituted an acute danger requiring immediate enforcement. These points are why the court found the parents' appeal against immediate enforcement to be justified. Basically, the first document focuses more on the immediacy with which the school suddenly wanted to expel the student plus whether the suspensive effect is granted or not, whereas the final document focuses more on whether the way the student used the glasses already fulfills legal grounds for an expulsion in general. They found that it does not. In the second publication from August, the court overall reiterates its earlier decision and shares a little more details from the pedagogical assessment. It reveals that there are also unanswered questions around medical necessity of the glasses, and the student's ADHD diagnosis (but these are neither the point of the decision nor this blog post, so we shall skip that). The news about this case usually underline the fact that the student somehow "won" and is now allowed to just keep wearing them. That's cutting it a bit short for clickbait reasons. The court decisions expressly state that its rulings on the suspensive effect against the expulsion and whether the behavior fulfills ground for expulsion does not actually decide anything on the legality of smart glasses in classes or anything about how valid or not valid certain privacy concerns are . Under Austrian administrative procedural law, an authority seeking to exclude the suspensive effect of an appeal (which they did here - they denied the appeal of the parents, which would have normally suspended the decision, and went for immediate enforcement) must justify why this immediate enforcement is urgently necessary, taking account of the interests of all parties involved. Kids and teens have a big interest in continuing school and are required to be there, which is why a lot of importance is placed on not excluding any student willy-nilly. § 49 of the Austrian School Education Act (Schulunterrichtsgesetz, SchUG) requires a serious breach of student duties, with the relevant educational or house-rule measures having failed, or a continuing danger. The existence of a public interest in school safety does not, by itself, establish that immediate enforcement is required, and a potential privacy risk isn't automatically be treated as proof of misconduct that fulfills the standards of § 49 SchUG. Related: Austria also has a Schulordnung that deals with tech use at school, specifically smartphones, smartwatches and similar, in § 7(6), (7) and (8) . It's unclear how old the student is, as the limit only applies to people below the 9th grade, but it at least gives grounds to see the use of smart glasses as analogous to what has been banned here for a portion of students already, so it is not as huge of a jump to possibly ban smart glasses at school entirely at some point. However, the court decision is explicitly not an endorsement of the student's conduct . So for now, the case actually doesn't support a view that students have an unconditional right to wear smart glasses during lessons, or that schools are prohibited from restricting smart glasses. Thankfully! So what's the path forward? A revision of the court decision is not possible. The school, as previously mentioned, had already had revised its rules, but that doesn't help when a student repeatedly doesn't comply and his infraction isn't deemed grave enough to allow for an expulsion. Other disciplinary measures have yielded no results. News report that the student is now socially shunned in school, but not even that is deterring him. He has the court decision printed out and shows it to anyone challenging him to take the glasses off. Austria's Ministry of Education is allegedly examining a possible general prohibition on smart glasses and similar devices in schools both due to privacy concerns and cheating in exams. I am not sure how this could weigh heavier than the school's house-wide rules, but it may give schools more of a justification in their disciplinary measures. The school is put in a difficult situation in which it needs to protect the rights of students and staff, yet is in no position to gather the required evidence to successfully argue for an expulsion. Schools are not allowed to check the devices of students and acquire any material, and therefore rely on students to voluntarily relay any evidence. But what student will incriminate themselves, especially when you want to keep doing what you do? It's especially frustrating here because the student himself has been caught recording with a different device before, showing he is capable of doing this. The behavior being caught on a phone and not the glasses was his saving grace. As no (proven or otherwise) crime has occurred, it's not like there is anything warranting police to confiscate the device and search for evidence. This may be the case when another student has enough evidence to suggest that they were recorded (maybe in a sensitive space like the school toilet or a changing room) and is reporting it to the police or otherwise pressing charges, depending on case and legal options. Though I don't think this would realistically be pursued successfully by any cop.... I empathize deeply with the staff and students. They have to tolerate this now until further developments, and it's unclear what those may be and if they would help getting this creep to comply. Smartphones have already added a sense of surveillance to the school, in the sense that things could be recorded and uploaded for millions of strangers online to see and ridicule. Now, this covert recording is easier than ever and adds to the chilling effects. They also have to deal with possibly unfair advantages during exams, as the student will likely keep arguing for medical necessity because he needs glasses to see and seemingly cannot be convinced to stop the use or exchange it for normal glasses. Especially as a woman, it frightens me. Lots of people, but especially anyone who went through puberty as a girl, can probably attest to the extreme casual sexual cruelty that teenage boys are capable of and so often employ against the girls in their class. In their position, I would be extra scared of any recordings that could amount to zooming in on their breasts and butts, or amount to upskirting, or any pictures that then later are used to nudify via AI. I have had partially nude pictures of me leaked at my school when I was 15, so I don't have to imagine - I know how it feels. I am also mad when I think back to how dress codes and school uniforms were handled when I was still in school. It was common to have rules around cleavage, shoulders, bra straps, length of skirts and shorts, the midriff, etc. or have school clothing that complied with these standards. My school employed a dress code, and I regularly violated it. I was either sent home or stuck into a shirt that was supposed to shame me for it. It was a confusing time. You quickly realize that these rules do not apply to boys, and that basically anything you could buy at stores for your age group and gender actually violated the dress code (especially the time when hotpants were in). I was advertised to with these non-compliant clothes; I was expected to wear them to be cool and pretty and have any worth as a woman, I was supposed to want them and buy them, yet at school we were treated as temptresses, as vulgar, unclean, pornographic, sexual. Something to be hidden away, something that doesn't deserve any expression or comfort, no matter how hot it got outside. The issue was always deemed to be us, not the male teachers or students lusting after us. It instills a sort of shame into you that is very, very hard to get rid of. Yet here we are, someone can just refuse to take off the Meta glasses, an actual real threat to people's rights and comfort, and everyone is apparently ultimately powerless to stop it! The double standards deeply irk me. The parents are also obviously failing their son. They give him a controversial toy without ensuring the appropriate handling of it and setting some ground rules. They know their child has ADHD, yet give him a device that easily lets him be distracted during class. They know something has to change, yet all they care for is keeping their child in school, and they aren't confiscating the glasses despite their child missing school over it, threatening his education and future prospects. They seem to accept that they have helped their son become a complete social pariah in his most formative social years. What a shame. A ruined teenhood, a risk to education, a threat to students and staff, court costs... for what? To make Mark Zuckerberg more rich and influential. It's not worth it. Published 11 Oct, 2026

0 views

Picks and Shovels

I've owned picksandshovels.org for the better part of a year. This afternoon, Claude Opus 5.5 built a web game for the site (with a little bit of inspiration from me.) As Claude was working, I interjected a bit of commentary, but pretty much all of the jokes and catastrophes are Claude's doing. Below is the sum total of my prompting to make the game. Happy clicking! Good luck avoiding the apocalypse!

0 views

Type-safe HTML templating in Zig

I’ve done a lot of web development over the years. I started around the year 2000, just after PHP 3.0 was released and started taking over the web world. I’ve used all kinds of approaches for generating HTML: raw PHP, string concatenation in Python, Smarty templates in PHP, Template Toolkit in Perl, and Jinja/Django templates in Python. Then, as the world moved on to SPAs and JavaScript frameworks, I stopped working on frontends. Two years ago, I picked up Zig, and I’d never have imagined I’d be considering web development in such a low-level language. But as I was building my own ecosystem with zio and Dusty , I realized it’s not that far-fetched. I built these libraries for API backends, where the output is JSON or MessagePack, but then I saw that with proper use of arena allocators, even web development is actually manageable, since you don’t need to remember to deallocate all the strings that are typically involved. And with Zig’s comptime, you can build very high-level interfaces without losing the performance benefits. And with the help of htmx or Datastar , you can even build SPAs without touching JavaScript/TypeScript, which I promised myself I’d never do. That said, one thing was still missing: templating. String concatenation was not an option, I didn’t want to go back to the pre-2000 CGI days. I had a few requirements for a templating library: I couldn’t find any solution that would satisfy all these requirements. Then I saw Ziex and I was amazed. It never occurred to me that the JSX approach could be done outside of JavaScript. The developer experience Nurul Huda managed to build in Zig was just unimaginable to me. It didn’t fit my needs directly, because I didn’t want a WASM framework, but the syntax and his approach to integrating Zig code into templates gave me some ideas. Then, after further research, I discovered Templ for Go, and it was pretty obvious to me what I wanted to build. So, let me present zt (I’m not good at naming, sorry) . It’s an adaptation of Templ to Zig. The templates are parsed and transpiled to Zig code during . That means you can import them directly from your app code, but also, perhaps even more importantly, the templates can import your app code. As a result, all parameters can be fully typed. If something can’t be rendered, the Zig compiler will tell you so. The generated code is fairly simple, all it does is write to a . You can write directly to a socket or accumulate the output in a buffer, your choice. Here is what a template looks like: And here is how you would use it from Zig code: If you want to use it inside Dusty to render an HTML page, you just do this: As a side note, the snippet above has a very non-obvious bug in error handling if you use the same pattern somewhere cancellation matters. I’ll probably write a blog post about it, but if you are interested, check out this Ziggit thread . The function in Dusty that wraps the handling solves it. All dynamic values are HTML-escaped by default, both in text and in attributes, and values in URL attributes like or are also checked for dangerous schemes. If you really need to output raw HTML, you have to ask for it explicitly with , so it’s easy to spot in the template. The parser also requires all elements to be properly closed, so you can’t accidentally produce broken markup. It won’t protect you from everything, but it removes the most common sources of XSS. What I like the most is that template errors are just Zig compiler errors. If you make a typo in the template above and write instead of , will tell you: The error points to the generated code, but each line has a comment with the location in the original template. The same applies to the calling side, if you try to pass a string where expects a , it won’t compile. Beyond the basics shown here, you can build reusable components and pass them around as values, and with you can build layouts that wrap other templates, which covers what you’d use template inheritance for in other languages. Control flow is just Zig, so , and work as you’d expect, including payload captures. All of it still compiles down to a sequence of writes, without any allocations. You can have a look at the TODO example in Dusty. It’s a simple htmx-based web app that showcases this library, plus other parts of Dusty that are needed for building a dynamic SPA. One part of the syntax is still open. Block-level control flow is written as plain Zig, following the example of Templ, so a line starting with , or is parsed as code. That means text like “if you forgot your password…” on its own line is a parse error, and you have to wrap it in an element or write it as a string expression. I’m considering switching to , and , similar to Razor or Twirl, which would remove the ambiguity, at the cost of slightly noisier templates. If you have an opinion on this, please leave a comment in this GitHub issue . Although multiple people are already using it, the project is not very mature yet, so I welcome any feedback. You can open a GitHub issue if you find any problems, or ping me on the Zig Discord. It has to be type-safe, with all inputs typed and checked at compile time. I’ve dealt with dynamic languages enough in my life, and I don’t want to deal with runtime template errors anymore. It has to be fast. No allocations, no intermediate state, just write the output to directly. It needs to have safe defaults. I don’t want to deal with XSS anymore, so all inputs have to be escaped by default. It should still feel like writing HTML. I didn’t want a Zig-based DSL I’d have to fight constantly.

0 views
Lea Verou Yesterday

The best way to use icons in Web UIs

I’ve always had a soft spot for icons 1 . Not only do they add polish to a UI, they can also tangibly improve usability. Familiar icons help users find what they need quickly, since we recognize pictures much faster than we read words. And as a web architect at heart, the current dominant ways we have to include icons on the Web make me sad. While there are many, many different ways to include icons in a Web UI, each with different performance characteristics , by far the most common ones involve adding inline SVG to the HTML. For example, a trash icon might be included like this: Some people use a separate SVG file for sanity and include icons via , but the markup is still fairly clunky (not to mention slow, sans bundlers). All this clunkiness forces people to resort to hiding the icon under layers of abstraction, such as dedicated custom elements like or React components like , but the ugliness still ships under all these layers, and the browser still has to deal with it. Icon fonts did put icons in CSS ( is still how Font Awesome’s pseudo-element mode works), and a lot of their appeal was exactly that. But they came with a long list of issues : large downloads (even with subsetting), fallback glyphs when the font fails to load, screen readers announcing private-use codepoints, user font overrides (e.g. for dyslexia) wiping them out, and text hinting that was never designed for pictures. We threw out the baby with the bathwater: the problem was the font, not the CSS. Many of the attempts to move this to CSS still involve markup. The mask trick that most of them rely on is not new either: Chen Hui Jing covered it in 2021, and Iconify generates mask-based CSS for its entire catalog. But in all of these, which icon to use (and whether to use one at all) is still decided in the markup, via one class per icon; CSS only handles color. The closest I’ve seen is David Bushell’s 2024 post on including icons purely via CSS via generated content + masks, where the icon is picked in CSS, but every selector that wants to support icons needs its own chunk of CSS, and there is no way to override the icon on a case by case basis. I still think it’s superior to inline SVG in HTML, and it baffles me why it isn’t more popular. Per the principles outlined in my user effort article , a good icon system should: How does that translate to an icon system? Per consumers > producers , making icon usage ergonomic is more important than making icon definition ergonomic, but ideally adding a new icon to the icon system should not require much more than: And of course, it still needs to be reasonably performant — the existing success criteria don’t disappear! The dominant ways to include icons today fail to meet at least one of these criteria. Can we do better? CSS is the only web platform language that facilitates reactive aggregation : you declare what applies to what conditions (type of element, interaction state, media query, etc.) and the browser automatically applies the right styles when those conditions are met and cleanly removes them when they are no longer met, without affecting any other conditions that simultaneously apply to the element, and without forcing developers to manage any of these side effects manually. I personally think we are underutilizing this wonderful language by artificially restricting it to styling, but that’s a topic for another article. If we can nail icons via CSS, this gives us tremendous expressive power, with practically no loss of flexibility. Imagine being able to tweak icons based on… Additionally, if icons can be injected or tweaked via CSS, this means we can specify them on pseudo-elements, such as , , or , something no markup solution accommodates. Or even, imagine applying entirely different icon sets to the entire page (or a portion) without having to touch any markup! E.g. a section with a hand-drawn aesthetic could get a hand-drawn icon set, without affecting the rest of the UI. The possibilities are endless. Once you allow yourself to consider that this is possible, you will start seeing use cases everywhere. Including icons as direct s (especially with no wrapping abstraction to manage a shared registry) means there is no way to enforce consistency — anything goes. One page might use a Font Awesome trash icon, another might use a Lucide trash icon. I think the icons themselves should be a type of design token, managed by the design system, that UI components simply use, just like colors and gradients. Managing icons via CSS also allows us to define them alongside other design tokens (e.g. as CSS variables on ), rather than in a separate place. Suppose icons were CSS variables, and we could add an icon to an element simply by setting an custom property, e.g. by doing something like This means we could also make simple things easy via: while maintaining the ability to override the icon on a case by case basis by simply overridding the property. We can even adapt to state reactively: or user interaction, e.g. to communicate both where a link goes (GitHub) and what it is (an external link): And for completely ad hoc standalone icons where adding CSS would be more work than doing it inline in HTML, we can have an class: This makes it strictly more powerful than the markup approaches , with the only exception being that completely inline SVG allows potentially changing the SVG itself in arbitrary ways (e.g. morphing it into another icon or changing it dynamically as the result of user input). For those cases, we’d still need inline SVGs. But how many of your icons fall in that category? The core idea can be summarized as: Icons are inserted via pseudo-elements, which use the icon to mask a square with equal to the icon color. The referencing syntax I’ve settled on is: You could be more elaborate and also define properties like , etc, if you deem them useful! The beauty of this approach is its immense flexibility. Available icons are defined on as CSS variables, e.g.: One-off icons can still be used by setting e.g. instead of , but if most UI references registered icons, they will stand out like a sore thumb and will be easy to spot in a review. Then, to display these as icons, the crux is: For simplicity, I will only discuss / in the rest of this section, but everything applies identically to / and any additional ones. You may be wondering why I’m specifying the size twice instead of . Flex items stretch by default, and a stretched size is definite, so is ignored. Since icons so often live in buttons, and buttons are so often , an explicit is far more robust. Grid happens to be fine ( behaves as for items with an aspect ratio), but flex has no such exception. Unfortunately, you need to repeat the pseudo-element contents for every type of pseudo-element you want to be able to support icons on. Mixins will eventually fix that. As described above, this would apply to all elements, which is far from ideal! We can use a style query ( finally Baseline! 🎉 ) to restrict this to only elements with the property set: We would also need to register as non-inheriting via , otherwise any container with an icon would sprout an icon for every single descendant (including inline formatting like , etc!), which we definitely don’t want. And then just explicitly inherit them in the pseudo-element: Style queries are Baseline 2026 , which sounds quite cutting edge. However, if you look at market share, it is supported in ~93% of users’ browsers worldwide per Can I Use data. While this data under-represents old Chrome Android (it lumps all Chrome Android under its latest version), Chrome shipped style queries in 2023 (in version 111) and Chrome Mobile users do seem to upgrade pretty quickly , so the real number won’t be meaningfully lower. How is this possible? If you sort by date, you will see that all major browsers except Firefox had shipped style queries by 2024. Firefox was the last one to ship them, in May 2026. Now, let’s see the Usage relative chart: Firefox as a whole accounts for 2.28% of worldwide market share. Now, I’m not saying ignore Firefox because of its low market share! That would be a d*ck move, comparable to making UIs inaccessible because screen reader users are a minority. Some things matter more than their raw numbers might indicate , and not excluding Firefox is essential for the Web to remain open, interoperable, and not controlled solely by large companies. That said, for features that have shipped in every major browser, I think it’s defensible to take market share into account. Baseline tiers are a great …baseline, but no compact metric could possibly convey this kind of granularity. Especially for something like this, where things generally still work fine without icons, it’s a plausible strategy to treat icons as a progressive enhancement and just accept not having them in the 7% of browsers that don’t support them. For the cases where icons are crucial, (e.g. icon-only controls) we can use a class (e.g. , , , etc), so that older browsers with no style query support still get them. Then, how much you want to lean in to progressive enhancement depends on how widely you rely on the class vs the style query. You could even add other known selectors to it e.g. inline styles or known selectors with icons: Do note that unrestricted attribute selectors like that can be slow, so use them judiciously. One issue with the class approach is that it hardcodes icon presence , even if you can still tweak the icon itself via CSS custom properties. One way to deal with this is to specify a value that becomes IACVT if the custom property has no value, which would make the entire pseudo-element disappear: This will resolve to a transparent image if is defined, and to → if it is not, which will effectively remove the pseudo-element. And yes, is Baseline Widely Available . It doesn’t remove any of the other properties, so it still cannot compose with other styles, so don’t apply it so broadly it can clash with other elements that may have styles. The styles above are a start, but we can do better: Putting it all together so far, we get something like this If you’re viewing the demo in Chrome or Firefox, it should work as expected. If you’re viewing the demo in Safari, you’ll already know that it’s severely broken. My research on this resulted in filing not one, but two Safari bug reports. The good news is that all have workarounds. The bad news is that these workarounds force us into suboptimal code, and unless we’re willing to ship a different version to Safari, everyone suffers. WebKit bug #301609 was the one that really threw a spanner in the works the most for this technique: The tl;dr is if you use what I wrote above, the icons will not show up in Safari until the user interacts with them in some way (e.g. hover, focus, etc.). Thankfully, I found a CSS workaround: However, since this applies an animation to all and pseudo-elements, ideally we’d want to restrict it a bit, and make it easier to override by wrapping it in a layer. Turns out this seems to magically also fix WebKit bug #320220 , which is also relevant here. At this point our demo works almost perfectly! Can you spot the bug that remains? There are two buttons that change their icon on hover: Default icon and Github to link . The former works, the latter doesn’t. WAT?! So, there is a difference between them: sets on hover, whereas Github to link sets directly, via . And is registered as non-inheriting, which is quite relevant here. Turns out that used to not work at all in Safari until 2023 , and there are still some cases where it reproduces today. The investigation resulted in WebKit bug #325900 being filed. There are a few solutions here of which the only workable one seems to be to register the property as inherited after all, and add a style query that resets it on descendants: It’s a bit more fragile and likely slower, so ideally we’d only ship it to Safari. can only detect features, not bugs, so once more we have to resort to sniffing WebKit. Conveniently, is allowed inside conditional rules, and when a property is registered multiple times, the last registration wins: The layer means any unlayered declaration wins over the reset, even zero-specificity ones like . Putting it all together, here is the final demo, with both workarounds applied: When using icons from SVG files it’s important to remember that any relative URL will resolve relative to the CSS file that uses the icon, not the one it’s defined in . In practice, this forces you to use root-relative URLs (e.g. ), otherwise you’ll have random breakage in any nontrivial project structure. An alternative is to register the icon properties as via and specify their values via : This is also better for other reasons: The obvious downside is DX: there is a lot more boilerplate to define an icon this way. The earlier syntax can be managed by hand, even for many icons. This starts requiring a build step after a dozen or so. Additionally, registered properties are currently global and don’t work in shadow roots , so that may not fit every use case. Another option might be to go through all properties in and register them via JS: An even cleaner option, if you know which stylesheet includes the icon tokens (and it’s same-origin) is to use the CSS OM to actually edit it, so that the declarations are removed once you register the properties: Since the icon is a mask, this technique only works for monochrome icons (varied opacity is fine) — but most icons are monochrome these days anyway. If you really need more, Allegro’s engineers stack masks on the element and both its pseudo-elements, which gets you up to three colors from a single element. But what about full color icons, e.g. emojis? Or textual icons, e.g. …emojis? In theory, the technique can work fine with them, these are actually easier! For full color icons you just set them as background images, no masking required. And for textual icons, you can just set them directly on the property. The only challenge here is branching between these three. But this may be the subject of a future post, as this is already getting quite long. Since the entire pseudo-element is masked, you cannot apply a separate background or border to it. For that, you’d need an actual element (e.g. ), and you can apply the borders, backgrounds, etc to that. Completely arbitrary animation would obviously still require inline elements. But what about predefined animations? A checkmark or X being drawn, a hamburger animating to a close button, etc.? I thought I had figured out a way. And I did get so close! Close enough that the whole saga deserves its own post, so stay tuned 😉 The system above gets us pretty good DX (especially without the Safari bugs!), but some things are still not ideal. First, the masking approach does introduce some limitations it would be nice to be able to get rid of. Then, signal-to-noise ratio could be even better: And while consumers > producers, it would be really nice if adding support for new pseudo-element types did not require such a song-and-dance. A number of upcoming CSS features get us closer to this North Star . If you haven’t heard of the new CSS Linked Parameters spec, it’s the best thing that has happened to CSS icons in a long time. It allows us to pass arbitrary data to any resource that supports it (and yes, SVG is one of them), which means we can customize icons in ways that were previously impossible, such as changing the stroke width, color, or even entirely arbitrary customizations, since SVG also supports style queries. And as of a couple weeks ago, it also supports passing and without any double opt-in from the icon, which means we can use any existing icon library without it having to be updated to utilize them! There are some limitations still, but it is being actively designed, Firefox has shipped an earlier version of the spec , and is one of the top Interop 2027 proposals , so I’m quite hopeful it can make it much easier to use icons in CSS. Keep a close eye on that one! Currently, due to a technicality in how the property is handled, we have to rely on background images to include icons, because otherwise and don’t work. Even Link Parameters by itself won’t change that, it will just allow us to get rid of the masking hack. Thankfully, we (CSS WG) recently resolved on a change that will fix this! (pending compat analysis) is the CSS feature you didn’t know you were desperate for, and deserves an article of its own for its myriad of use cases in design systems. Until I write that, Bramus has a great write-up . But more relevant to icons, once is widely supported, we can construct variable names dynamically, meaning we can use plain identifiers instead of CSS variables, which will make the syntax even cleaner, meaning we’d be able to translate into at the cost of a little complexity on the producer side: Yes, it’s ugly, but it’s also an implementation detail that’s not exposed to icon consumers, so I think the tradeoff is worth it. Combined with expanded , which is getting there with only Safari remaining (Safari TP already supports it), this also unlocks ad hoc icons via a plain attribute: We recently resolved to support registering multiple properties with the same descriptors at once via , this won’t help much here, since we need different initial values. You could of course register multiple properties with the syntax and then also set their actual icons on but now you need to add each icon in two places, so I’d consider it worse than the current expanded syntax. However, another direction we are exploring in the CSS WG is a shorthand form of that would allow more compact registration. For the icon above it would look like this: Currently, we have to repeat a lot of styles between the different pseudo-elements. If we want to also have a fallback class that doesn’t depend on style queries, we need to repeat them four times! With we can just do: And then we can use them all over the place: I published a package that puts together everything from this post, polish and warts included. It contains no icons, just the icon system. You’d just include it and then you can use and in your styles. It also includes the , , and classes. It does not wrap its contents in a layer to give you maximum choice, but I’d recommend importing it in a layer anyway. @nudeui/icons There’s a small chance I may have been the first to use a web font for UI icons , in 2010. I even worked as Product Lead at Font Awesome for a bit. ↩︎ I’m using “components” here in the general sense of a reusable UI element, not in the sense of a framework component or a custom element. E.g. a styled or an would qualify. ↩︎ keep syntax close to the minimum necessary to declare intent , and make simple things easy, complex things possible, and the transition from one to the other smooth (no usability cliffs) High signal-to-noise ratio: The intent to add or override an icon is basically “icon” plus the icon name, and possibly its location (start, end, marker, etc). Anything beyond that is boilerplate. Some boilerplate is unavoidable, but most -based solutions involve unacceptable amounts of boilerplate (which is why we end up abstracting that away). Simple things should be easy: We should be able to define default icons for certain cases without having to explicitly include them at every callsite. E.g. a should automatically get a trash icon, without needing to declare it in the markup in any way, shape, or form, unless we want to override it. The default icon color should follow the local text color (or designated icon color, if different) without any additional song and dance. Complex things should be possible: We should be able to easily override the default icon for a component, either permanently or as a result of various conditions (state changes, user interaction, etc.). Styling the icon should be as easy as applying a CSS property. The minimum is color, but the more extensive styling is possible, the better. No usability cliffs: Overridding an icon, temporarily or permanently, should still not require much more than the bits of information necessary to declare intent. declaring the name of the icon, and providing the SVG path data or a URL to an SVG file Any selector, including interaction pseudo-classes ( , , etc.), e.g. to communicate additional information about the action or adapt to a different background color Media queries ( , dark mode, high contrast, etc), e.g. use different icons in dark mode, add or remove them in print, etc. Container queries Writing mode, e.g. to use a different icon for RTL text Icons are first-class design tokens stored in properties that hold values (embedded or remote) Icons are inserted via pseudo-elements. The icon is applied as a mask to a square with equal to the icon color. The system uses general CSS custom properties like , , and for flexibility and reusability. Style queries + for conditional icon insertion based on these custom properties. Low-level properties: to prepend an icon to element contents (via ) to append an icon to element contents (via ) Optionally, additional properties can redirect to other pseudo-elements, e.g. for higher level abstraction that just means “add an icon to this component”: components 2 can redirect it to any of the lower-level properties on the element itself or a descendant depending on what makes sense for that component. E.g. may set on a but on An empty pseudo-element e.g. or is used to display the icon The pseudo-element is painted as a square with A CSS masks the square in the shape of the icon ( But, but, but …browser support?!? Fear not! Masks are actually Baseline Widely Available nowadays) A CSS property ( or ) is used to specify the icon to display, instead of an ad hoc one like . This means we can set any element’s icon from any place that accepts declaration, including via inline styles Customizable color and size : Rather than hardcoding and , read them from custom properties with those as fallbacks: and (same for ). Since they inherit, you can style every icon in a subtree by setting them on an ancestor, without ever touching the pseudo-element. The prefix keeps them from colliding with the icons themselves, which are also . Preserve in forced colors mode : to keep the mask fill in forced-colors mode. will still follow the forced text color. Don’t get in the way of pointer interactions : so that it doesn’t trap clicks it shouldn’t. Don’t shrink in flex containers : . An empty pseudo-element has a zero minimum size, so in a flex button it is the first thing to shrink when the label is long, and it can shrink all the way to zero. An icon’s size is a design decision, not something layout pressure should negotiate. Vertical centering : I add to center the icons and nudge them a tiny bit upwards (our eyes weigh the space above the icon more than the space below it, so if something is perfectly centered mathematically, it will look a bit low). With a customizable size, that becomes , so that the icon’s center stays put as it grows. Spacing from text : to add a bit of space between the icon and the content ( for ). Make sure to remove it for standalone icons. Accessibility : The pseudo-element has no text, so assistive technology ignores it entirely, which is exactly what we want for an icon next to a label. Icon-only controls still need a name, and we can let the icon provide one via alternative text for generated content : , so that is announced as “Delete”. on the control itself still works, of course. There won’t be hundreds of variables in dev tools for You can even override it temporarily with another icon for a specific context, and reset it wherever via What if could work, instead of ? What if could work, instead of ? (ignoring HTML restrictions around attribute names for a bit) There’s a small chance I may have been the first to use a web font for UI icons , in 2010. I even worked as Product Lead at Font Awesome for a bit. ↩︎ I’m using “components” here in the general sense of a reusable UI element, not in the sense of a framework component or a custom element. E.g. a styled or an would qualify. ↩︎

0 views