\n\n(Apologies for the preroll ad. Click here to go direct to be original post.)\n\nThe perennial question of racism and sexism in computer games can easily be answered:  yes, computer and video games are racist and sexist. But then so is everything else. Video games are way too easy a target, there's no game I can think of that even comes close to not being stupidly sexist and/or racist, unless it has no human characters at all. But how many current network TV shows have a central character that is non-white where their non-whiteness is purely incidental, i.e. they just liked that actor, or whatever?\n\nMy favorite TV show right now (and since I started watching it) is House, which has a not-quite-central character (Foreman) who is black, and another (Kuttner) who is a classic urb (\"uncertain racial background\" -- a term I coined in 1990 which doesn't seem to have taken off) -- perhaps because urbs have almost become subsumed into \"white\" in the US. In House, House frequently makes ridiculously racist remarks. In the unlikely event you haven't watched the show, House is an equal opportunity offender, sexually harassing a coworker one minute then using racial slurs and stereotypes the next.\n\nA randomish sampling of shows: The Office -- white (some minor/guest characters are non-white); Bones -- white with a black woman boss and an urb woman artist, both improbably attractive; Battlestar Galactica -- white with minor non-white characters; CSI -- white with a black guy who has gambling problems and grew up in gangland; Castle -- white; Saving Grace -- white with black woman boss and native American detective (not bad!); The Closer -- latino, three black, and a Chinese major character; Damages -- white; Dollhouse -- white with black handler and Eurasian roommate.\n\nThe Closer and Saving Grace (both TNT cable shows) are the only examples that don't seem contrived or just plain white. Along with House and Dollhouse this is about as good as it gets.\n\nOh and what is it about Asian (or part-Asian) actresses with Australian accents? They seem to be The Hotness in Hollywood right now (there's one in Dollhouse and another in Terminator right now).\n\nSometimes you see the reverse, e.g. in a lot of police shows there is a heavy skewing towards white perpetrators, even when (based on where a crime takes place, say) the chance that the perpetrator(s) would be white is pretty small. It's pretty rare to find a show like The Wire where the mix of race and sex seems pretty much realistic, rather than thoughtlessly white (Friends had two \"Jewish\" characters) or otherwise monoracial (like the all-black sitcoms of the 70s).\n\nSo, basically, we may have a non-white president, but we've a long, long ways to go.","$updatedAt":"2024-06-05T09:25:07.080+00:00",path:"capcom-aren-t-bad-people-they-re-just-idiots",_created:"2024-07-09T20:32:46.818Z",id:"669",_modified:"2024-07-09T20:32:46.818Z","$id":"669",_path:"post/path=capcom-aren-t-bad-people-they-re-just-idiots"},latestPosts:["one-user-interface-a-curb-cut-for-everyone","one-world","how-i-learned-to-stop-worrying-and-love-web-1-0","oulus-bike-parking-problem","great-ideas-don-t-shake-the-world-they-wobble-it","tosijs-icon-system"],recentPosts:[{_path:"post/5hbqt111s52y",title:"One User Interface—A Curb Cut for Everyone",date:"2026-08-28T12:13:36.778Z",summary:"Curb cuts were built for wheelchair users and ended up helping everyone. Agent interfaces are the same shape—but today's approaches all maintain a second description of your app beside the app, which is the documentation problem all over again. tosijs derives the agent surface from the live state-to-DOM map it already maintains, so the map can't go stale, accessibility hints enrich it for free, and when the map looks wrong, the app is wrong. Cost: under 7kB gzipped.",keywords:[],path:"one-user-interface-a-curb-cut-for-everyone"},{_path:"post/rd7xk3qqp020",title:"One World",date:"2026-07-30T19:26:23.004Z",summary:"Building UI for 3D and VR is historically a fragmented nightmare, propped up by garbage legacy abstractions like Scaleform. tosijs-3d fixes this by stripping out the friction entirely.\n\nInstead of maintaining three separate UI layers, it leverages tosijs's existing SVG bindings, renders them to a canvas, and projects them directly as 3D textures. On the input side, everything—VR controllers, keyboards, touch glass—gets routed through a unified, GTA-style gamepad model.\n\nThe result is a single, deterministic interface. You build it once, debug your VR UI directly in the standard DOM, and stop tearing a headset on and off just to fix a basic bug. One world, zero friction.",keywords:[],path:"one-world"},{_path:"post/0vm2hnx6rt00",title:"How I learned to stop worrying and love Web 1.0",date:"2026-06-20T19:53:23.109Z",summary:"After years of trying to fight the good fight to make dynamic pages SEO-friendly, LLMs using curl have basically forced me to embrace pre-rendered HTML. Turns out I accidentally already had the perfect architecture.",keywords:[],path:"how-i-learned-to-stop-worrying-and-love-web-1-0"},{_path:"post/ahi5oipr7an0",title:"Oulu's Bicycle Parking Problem",date:"2026-05-13T08:51:28.866Z",summary:"Oulu's a bike friendly city with a bicycle theft problem. Part of the problem in my opinion is actively terrible bike rack designs.",keywords:[],path:"oulus-bike-parking-problem"},{_path:"post/z76j9nkh3sze",title:"Great Ideas don't shake the world, they wobble it",date:"2026-04-28T07:21:46.642Z",summary:"I was really annoyed by this article in IEEE Spectrum placing the Xerox Alto at the center of modern technology. Like any tech nerd, I know the trope that Xerox PARC's work is \"forgotten.\" In fact, the idea that PARC is forgotten is so common, I think it's actually remembered a little too well. The \"great man\" theory of technology is not a simple dichotomy between Steve Jobs being a genius and Steve Jobs being a thief.",keywords:[],path:"great-ideas-don-t-shake-the-world-they-wobble-it"},{_path:"post/0n7zm0jkoztv",title:"the tosijs icon system",date:"2026-04-26T08:56:15.559Z",summary:"tosijs-ui's icon system has already solved all the problems every other icon system deals with (and few if any of which deal with completely) without needing build magic. Now it adds the ability to compose and tweak icons on-the-fly.",keywords:[],path:"tosijs-icon-system"},{_path:"post/9mhabwz7tk9k",title:"Making hell less hellish encourages more time in hell",date:"2026-04-24T06:13:08.852Z",summary:"It is possible to have too much of a good thing, and if that good thing has small but non-trivial costs that aren't borne up front and the good thing becomes cheap or free, you can end up being overwhelmed by the costs later.\n\nSound kind of abstract? If dependencies become free we end up with tends of thousands of dependencies. If configuring servers becomes free we end up shipping fragile code that \"works for me\". And here we are.",keywords:[],path:"making-hell-less-hellish-encourages-more-time-in-hell"},{_path:"post/1azcru685z77",title:"How I built new Javascript Superset for $250 in about two weeks",date:"2026-03-04T14:58:57.254Z",summary:"tjs-lang is a transformative JavaScript superset and \"agent-native\" dialect designed to reclaim the language's Lisp-like heritage while providing the runtime safety that TypeScript only pretends to offer.",keywords:[],path:"how-i-built-new-javascript-in-two-weeks-for-250-dollars"},{_path:"post/22es2bz3ipbz",title:"tosijs-schema is a schema-first type library",date:"2025-11-21T19:31:51.896Z",summary:"Ever wished for a universal language for describing data? I once built a JavaScript tool to do just that, but it never quite caught on. Now, it seems JSON Schema is stepping into the spotlight, and I've taken inspiration from it to create `tosijs-schema` – a tiny, dependency-free library that allows you to create schemas, infer them as TypeScript types, and validate them in O(1) time.",keywords:[],path:"tosijs-schema-is-schema-first"},{_path:"post/z66tob4g71c4",title:"Walking on broken glass…",date:"2025-11-07T18:49:33.561Z",summary:"At least since the return of Steve Jobs, Apple has had a both the technical chops and the desire to make UIs that are both brilliantly executed and hard to replicate. In its day Aqua was intimidatingly impressive technically, even if it was garish. But their new \"glass\" UI is as subtle as it is impressive.",keywords:[],path:"walking-on-broken-glass"},{_path:"post/07nqt8lro2kp",title:"react-tosijs is live",date:"2025-11-06T17:50:02.365Z",summary:"react-tosijs 1.0.2 is out and replaces react-xinjs. As before, it provides useTosi() (and useXin()) to allow developers to manage their React application state with tosi, thus eliminating the need for providers and tangled hooks (or—shudder—redux). But now it also provides the reactWebComponents proxy for turning web-components into React functional components with a single line of code. It's like magic.",keywords:[],path:"react-tosijs-is-live-now"},{_path:"post/uvgjmiqp2n3p",title:"The Owl has Landed",date:"2025-10-21T18:51:35.405Z",summary:"tosijs is a from-the-ground rebuild of b8rjs that matches its ambition but is written in Typescript, meets more rigorous engineering standards, and has a simpler and more powerful API.\n\nThis summarizes the five core capabilities and benefits tosijs brings to the table, allowing you to just write javascript but get more done with fewer dependencies than any other framework.",keywords:[],path:"the-owl-has-landed"},{_path:"post/0cs6kyt7zm9r",title:"CSS.registerProperty and @property considered harmful",date:"2025-06-27T17:07:10.275Z",summary:"I love CSS variables, but this new feature of CSS makes them fragile and there's no obvious workaround. Don't use it.\n",keywords:[],path:"css-registerproperty-and-property-considered-harmful"},{_path:"post/s51blf4tsx65",title:"What should a front-end framework do?",date:"2025-06-03T22:24:57.708Z",summary:"This article introduces tosijs, a highly opinionated front-end framework designed to radically simplify complex web and desktop application development. Unlike frameworks that complicate simple tasks or rely on inefficient virtual DOMs, tosijs leverages native Web Components, direct DOM manipulation, and a unique proxy-based state management system to minimize boilerplate, boost performance, and enhance maintainability, allowing developers to achieve more with less code.",keywords:[],path:"what-should-a-front-end-framework-do"},{_path:"post/nh3jt0g2b6wl",title:"Squircles",date:"2025-05-05T16:14:46.879Z",summary:"Tired of those boring rounded rectangles? Squircles are the next evolution in smooth, visually appealing shapes, and while perfect execution is tricky, Amadine's does a nice job with them.",keywords:[],path:"squirrels"},{_path:"post/5lxsjwunwnjb",title:"Blender Gets Real",date:"2025-03-26T15:04:54.659Z",summary:"Flow, the Blender-animated film, took home the Oscar for Best Animated Feature. But it's more than just a win for a small team; it's a monumental victory for open-source software and anyone with a vision and a limited budget.\n",keywords:[],path:"blender-gets-real"},{_path:"post/2n8p4qfong3r",title:"The future's so bright… I want to wear AR glasses",date:"2025-02-04T17:57:55.814Z",summary:"So much bad news right now…\n\nIt's all a huge shame, since technology is making incredible strides and it's incredibly exciting. Sure, we don't have Jetsons-style aircars, but \nhere's a list of stuff we do have that's frankly mind-blowing.",keywords:[],path:"futures-so-bright-i-want-to-wear-ar-glasses"},{_path:"post/pcczhmzc23hk",title:"Contrary to popular belief, AI may be peaking",date:"2025-01-21T19:47:46.193Z",summary:"Is artificial intelligence actually getting *smarter*, or just more easily manipulated? This post delves into the surprising ways AI systems can be tricked, revealing a disturbing parallel to the SEO shenanigans of the early 2000s. From generating dodgy medical advice to subtly pushing specific products, the potential for AI to be used for nefarious purposes is not only real but the effects are already visible.",keywords:[],path:"peak-ai"},{_path:"post/k1p7e2gn552k",title:"Large Language Models — A Few Truths",date:"2025-01-17T20:25:13.992Z",summary:"LLMs, like ChatGPT, excel at manipulating language but lack true understanding or reasoning capabilities. While they can produce acceptable responses for tasks with no single correct answer, their lack of real-world experience and understanding can lead to errors. Furthermore, the rapid pace of open-source development, exemplified by projects like Sky-T1-32B-Preview, suggests that the economic value of LLMs may be short-lived, as their capabilities can be replicated and distributed at a fraction of the initial investment.",keywords:[],path:"large-language-models-a-few-truths"},{_path:"post/ui4rtvaqo6vv",title:"Adventures with ChatGPT",date:"2025-01-17T19:55:24.473Z",summary:"ChatGPT excels at mundane coding tasks, akin to a bright intern who reads documentation and StackOverflow but lacks creativity and testing. While useful for automating repetitive tasks, its code requires refinement and testing.",keywords:[],path:"adventures-with-chatgpt"},{_path:"post/xohqdwdgjlxa",title:"Apple Intelligence—Image Playground",date:"2025-01-15T18:14:38.350Z",summary:"Apple's new Image Playground is focused, and easy to use. If you want to produce cute \"Pixar-style\" people and animals, it quickly churns out consistent, but very limited, results. My M3 Max rendered images in seconds, but right now it's more of a cute toy than a useful tool\n",keywords:[],path:"apple-intelligence-image-playground"},{_path:"post/wl9ydyiwzvzl",title:"Bambulabs P1S Initial Review",date:"2025-01-03T14:19:55.667Z",summary:"Unboxing a BambuLabs P1S 3D printer was a surprisingly involved process, and the journey didn't get much easier from there. This Chinese contender for the \"Apple of 3D printing\" boasts impressive hardware, but the software experience can be pretty frustrating. From fiddling with hidden allen keys to battling Bluetooth connectivity, the setup was a minor ordeal, and navigating the app was a test of patience. But the truly game-changing moment? Snapping together perfectly printed hex tiles on the first try.",keywords:[],path:"bambulabs-p1s-initial-review"},{_path:"post/x4ut4tag0iil",title:"Design for effect",date:"2024-12-31T10:09:36.000Z",summary:"Star Citizen's 1.0 announcement has me scratching my head. The creators seem hellbent on simulating everything, ignoring a fundamental truth about game design—simulating reality doesn't guarantee good gameplay.\n",keywords:[],path:"design-for-effect"},{_path:"post/3y2c5bcp8tyl",title:"Taste is the secret sauce",date:"2024-12-30T18:35:28.600Z",summary:"The Mac, in its early days, seemed to diminish the value of artistic skill. But the reality was more nuanced. It amplified *taste*, making beautiful things achievable for those with taste, and terrible things easy for everyone else.\n",keywords:[],path:"taste-is-the-secret-sauce"},{_path:"post/ucc7x3oj34mk",title:"Acorn 8 is out and it's fine",date:"2024-12-18T18:51:07.199Z",summary:"Acorn 8, a $20 Photoshop replacement, is out! While the new AI subject selection is not fantastic, other features like JPEG-XL and vector tools are solid, the UI for some elements feels a bit clunky. But ultimately, Acorn continues to reign supreme as my go-to bitmap editor, and I'll happily pay its price—and upgrade.\n",keywords:[],path:"acorn8-is-out-and-its-fine"},{_path:"post/o4ewuf8xvxpt",title:"Signing and Notarizing Tauri Apps",date:"2024-12-18T18:30:52.372Z",summary:"Frustrated with Tauri's convoluted developer credentials setup? This post breaks down the process into bite-sized chunks, using simple commands and visual guides to walk you through the often-confusing world of signing identities and notarization. From finding your correct signing identity to configuring environment variables within your `package.json`, here's a simplified process with illustrations which should help you distribute your installers.\n",keywords:[],path:"signing-and-notarizing-tauri-apps"},{_path:"post/242twlcv1jzs",title:"Dogfooding the xinjs stack",date:"2024-12-12T18:53:55.832Z",summary:"My WordPress blog, hosted on a dodgy shared server for over a decade, finally bit the dust. So I rebuilt it on Google Firebase using my own full-stack framework, xinie. This post is a test run, straight from my iPhone 14 Pro, and a chance to see how xinie performs in the field. Expect a few typos, but maybe, just maybe, some elegant code snippets too.\n",keywords:[],path:"dogfooding-the-xinjs-stack"},{_path:"post/tiayw0anhy08",title:"Tauri Revisited",date:"2024-11-30T14:05:11.368Z",summary:"Tired of Electron's bloat and endless API changes? I dove back into Tauri, a surprisingly lightweight Rust-based alternative, and was blown away. Tauri lets you build apps smaller than a lot of built-in Mac apps, lets you build snappy, secure executables, all while keeping things simple. Plus, the bundle size? Seriously impressive. Read on to find out how I got shell commands working with Javascript, and why this might just be the web-to-native solution you've been waiting for.\n",keywords:[],path:"tauri-revisited"},{_path:"post/efj4nj5y4ebl",title:"Further Thoughts on the Meta Quest 3 and Vision Pro",date:"2024-11-28T18:41:20.430Z",summary:"XR devices like the Quest 3 and Vision Pro promise a revolutionary way to work and create, but I've found a frustrating reality. They're surprisingly limited in their standalone capabilities, forcing developers to settle for compromises and workarounds. This post dives into the specific pain points I've encountered, from clunky text entry to the limitations of web-native development stacks, and how the current state of XR development stacks falls short of the potential.\n",keywords:[],path:"further-thoughts-on-the-meta-quest-3-and-vision-pro"},{_path:"post/1bmtchcg6uxo",title:"Amadine is my new favorite vector graphics editor",date:"2024-11-18T17:04:56.213Z",summary:"Tired of clunky vector graphics apps? Amadine, a new Mac App Store gem, might just be the lightweight, elegant solution you've been searching for. While not flawless, it surprisingly nailed a design challenge others consistently missed. Is this the vector editor we've been waiting for? Find out more in my full review.\n",keywords:[],path:"amadine-is-my-new-favorite-vector-graphics-editor"}],blogDataTimestamp:"2026-09-11T17:53:42.209Z",blogVersion:4,"post/path=one-user-interface-a-curb-cut-for-everyone":{title:"One User Interface—A Curb Cut for Everyone",path:"one-user-interface-a-curb-cut-for-everyone",content:"\n\n\"person\n\nJust as curb cuts for wheelchairs ended up benefiting cyclists and people with rolling luggage, designing a solid user interface architecture for one platform gives you a massive head start everywhere else.\n\nBut now, user interface designers have inherited a completely new type of user: Agents.\n\nMy mental model of an AI agent navigating a *graphical* user interface is a blind polymath who speed-reads braille. They don't \"see\" things; they read descriptions of things. Ideally, *structural* descriptions. When you realize this, the way we currently force agents to interact with web apps is absurd.\n\n## The Status Quo: Guessing the Floorplan from Street View\n\n\"street\n\n> Agents currently try to navigate apps by surface detail. It's like trying to infer the floorplan and wiring of a house from street view. I used to live in this house and I can't figure out what went where from this.\n\nMost agents still interact with browsers through Playwright scripts, CDP, or visual screenshot loops. They treat the web page as an opaque, external artifact. They are either trying to \"see\" pixels—which requires jumping through heavy WebRTC security hoops and dealing with visual noise—or they are reverse-engineering an unholy mess of mutated HTML nodes.\n\nIf you've ever tried to extract semantics from the DOM via the console, you know it's a nightmare. This is why tools like my [haltija](https://www.npmjs.com/package/haltija) library exist: to help agents translate what's displayed into highly optimized tokens. But it's still fundamentally a hack. You are forcing the agent to play text-based archaeology on a rendered page.\n\nThe industry noticed. **WebMCP**—a W3C proposal from Google and Microsoft—lets a page hand agents typed, callable tools directly, and it's real: a public Chrome origin trial, a model context API on the page (the spec says `document.modelContext`; the origin trial still ships the deprecated `navigator.modelContext`, so implementations probe both), default-on across Shopify storefronts, and one dashboard toggle away for any site behind Cloudflare.\n\nSo the interesting question is no longer *should the page tell the agent what's possible*. That argument is over. The question is **where that description comes from, and what it costs to keep true.**\n\n## The 3D Epiphany\n\nThe sheer friction of this became obvious to me when I was building UIs for a 3D environment using my `tosijs-3d` library. Building UIs in 3D is notoriously painful, and debugging them with an agent using screenshots is nearly impossible due to those same security blockers.\n\nTo solve the 3D UI problem, I realized we could just build UIs in SVG, bridge the events from the 3D raycast world to the DOM, and update the texture as needed. Rendering SVGs into canvases is cheap and easy to serialize.\n\n\"tosijs\n\n> tosijs-3d implements a *unified user interface* using SVG that works across the DOM, \"flat\" 3D surfaces, and in VR. And it leverages the declarative binding logic of tosijs. You can [see this live](https://3d.tosijs.net/table/) if you're interested.\n\nSuddenly, debugging went into overdrive. Because I was giving the agent *what's actually there and matters* instead of a pixel screenshot or a messy DOM tree, the agent understood exactly what it was looking at.\n\n## We Built the House. We Know Where the Wires Go.\n\nThis led to a bolt from heaven regarding standard 2D web development.\n\nMy framework, [tosijs](https://tosijs.net), doesn't rely on parsing the DOM to figure out what's happening. You use an `elementCreator` to style, bind state to properties, and bind events to actions at the moment of creation.\n\nWould you rather write Vanilla JS that easily falls out of sync? Or React, which returns an opaque object that eventually renders? Or `tosijs`:\n\n```javascript\nexport const userButton = () => elements.button(\n { \n onClick: app.user.openConfig,\n style: { backgroundColor: vars.userButtonBg }\n }, \n app.user.name\n)\n\n```\n\nIn `tosijs`, if `app.user.name` changes, the button updates automatically. But more importantly, `tosijs` *remembers this mapping*. It maintains a live, two-way map from state to the DOM, and from the DOM to event handlers.\n\nBecause `tosijs` has this internal map, it can draw a schematic map of the app—skipping cosmetics—showing exactly where data is coming from and what actions are available.\n\n## Nobody Hand-Writes This Anymore. That's Not the Same as Getting It Free.\n\nEveryone is converging on the same instinct from different directions.\n\n**Cloudflare** takes what it can *see* of your site and turns it into something an agent finds easier to comprehend. **Shopify** actually solved it for their case—every storefront gets catalog, cart and checkout, no merchant asked—but that's Shopify's domain model, not yours, and it stops describing reality the moment a merchant customizes hard enough. **Angular** takes the usual approach: more boilerplate, one opt-in at a time.\n\nAll of this beats hand-writing a tool per button. But every one of them is a *second artifact*—another description of your app, maintained beside it. It's the difference between adding online help and fixing your user interface.\n\nWhich is a problem every developer already recognizes wearing different clothes. **It's the documentation problem.** Two descriptions of one system, kept in step by a promise—and that promise is always kept for a while and never kept forever. We already know the only fix: stop maintaining the second copy and generate it from the first.\n\nAn agent surface is the same shape. Derive it from the wiring the framework already holds and the map can't quietly go stale, because it isn't a copy. If it's wrong, the app is wrong—and *that*, someone notices.\n\n## Handing Agents the Floorplan and the Wiring Diagram\n\nWhen your framework already maintains this bi-directional map of state paths and executable actions, forcing an agent to inspect the page from the outside is ridiculous.\n\nInstead of making the agent scrape the DOM, calling `enableAgentInterface()` exposes `tosiAgent`—a global protocol-neutral surface—and registers a generated WebMCP tool set with the browser's model context (`document.modelContext`, with a fallback for the `navigator.modelContext` surface the origin trial still ships).\n\n\"tosiAgent\n\n[See this live](https://tosijs.net/derived-surface/)\n\nNothing is exposed until you ask, and the default only *looks*: a bare `enableAgentInterface()` gives read-only introspection, where `write()` and `call()` refuse and tell you how to enable them. Production is an allowlist—you name the state roots and actions an agent may touch, and a manifest scopes what can be *seen*; letting an agent *change* things is a separate, explicit grant. Point a schema at those roots and refusals come back with reasons, which is what an agent actually needs to correct itself.\n\nAgents don't need to *guess* where to click, take screenshots, or mine the DOM. They receive a diagram of what the application *is* (the wiring diagram) and (if you provide it) a curated map (the floor plan). The browser stops being a display output that an agent has to hack its way into, and becomes a structured, interactive partner.\n\n## A Virtuous Circle\n\nThis is where the curb cut becomes a virtuous circle. Because this schematic is derived from a single source of truth, any effort you spend improving the app for one audience automatically improves it for the others.\n\nWhen you add ARIA labels or accessibility hints to assist a human using a screen reader, `tosijs` absorbs those details and automatically surfaces them as richer schema descriptions in the map. Conversely, if you curate the map to clarify a messy action for an agent, you are forced to untangle your underlying state architecture—resulting in a more robust application for human users and a dramatically easier system for programmers to test.\n\nAnd it runs in a direction a scraper structurally cannot. tosijs ships an accessibility audit that runs over that same map: anonymous affordances, actions nothing can name, controls too small to hit, contrast failures, inputs labeled only by a placeholder that vanishes the moment you type.\n\nThat audit is only possible because the map holds *both sides*—what you declared the thing to be, and what it actually rendered as. A tool reading only the rendered page has nothing to compare against; it inherits your accessibility bugs and faithfully reports them as facts. **An integration absorbs discrepancies. An intrinsic surface prosecutes them.**\n\nSo this isn't \"we remembered to think about accessibility too.\" The mechanism that serves agents *is* the mechanism that finds the accessibility bugs. Same records, same pass.\n\nWhich matters more than it used to, because accessibility is currently *losing*. The 2026 WebAIM Million reports accessibility regressing for the first time in six years—95.9% of the top million home pages have detectable WCAG failures, page complexity jumped 22.5% in a single year, and misapplied ARIA is now correlated with *more* errors, not fewer. That same structure is what agents read. So the curb cut cuts both ways: semantics stop being a compliance checkbox somebody defers, and become load-bearing for a feature the business actually wants. Two reasons to fix it instead of one. Then the library has to hold up its end and make the fix cheap—declare a role and a description once on a component and they materialize as real ARIA, and the audit tells you which ones you still owe.\n\nInstead of doing extra, siloed work to support agents, the work pays dividends across the board.\n\n* **Humans** consume the visual DOM (or the 3D SVG projection), benefiting from a usable, accessible, responsive user interface.\n* **Code** consumes the centralized state registry, benefiting from predictable architecture.\n* **Agents** consume both the floor plan and the wiring diagram, all provided clearly and concisely.\n\nAnd the \"curb cut\" metaphor runs deep, even for the programmer. The agent surface reads the framework's own records, so when the map is wrong, something real is wrong. We found and shipped a core bug fix within hours of the map looking odd. If the curb cut doesn't look right, maybe the sidewalk has a problem.\n\nTesting with `haltija`'s shipped native tier, and Chrome's own `executeTool` in Canary, the agent successfully read the map, called actions, and updated the DOM without any vision or CSS selectors. And because the agent interacts via explicit contracts, every change, action, awaited condition, and refusal lands in a single audit log.\n\nTo be clear about where the ecosystem stands: the supply side of WebMCP is racing ahead of the demand side. The agents actually calling these tools today are mostly Gemini in Chrome, on Chromium, through an origin trial. I'm comfortable betting on the supply side anyway—because for tosijs apps, the bet is nearly free.\n\nWriting DRY code around a single source of truth benefits everyone—not just the end users, but the engineers themselves. It means less work today, and less code to read, maintain, fix, and keep in sync tomorrow.\n\nThe total cost to expose the information architecture of your app to agents in a form they can understand is **about 6.7kB gzipped**—or 10.6kB if you also ship the schematic renderer and the accessibility audit *(measured against tosijs 1.8.0 — TODO: re-measure against 1.12 before publishing)*. A better UI architecture for humans turns out to enable a \"curb cut\" for agents that's almost free—and ultimately benefits all users.",format:"markdown",date:"2026-08-28T12:13:36.778Z",keywords:[],summary:"Curb cuts were built for wheelchair users and ended up helping everyone. Agent interfaces are the same shape—but today's approaches all maintain a second description of your app beside the app, which is the documentation problem all over again. tosijs derives the agent surface from the live state-to-DOM map it already maintains, so the map can't go stale, accessibility hints enrich it for free, and when the map looks wrong, the app is wrong. Cost: under 7kB gzipped.",author:"Tonio Loewald",_created:"2026-08-05T06:57:34.683Z",_modified:"2026-08-28T12:22:09.547Z",_path:"post/5hbqt111s52y"},"post/path=one-world":{title:"One World",path:"one-world",content:"The thing I've been passion about my entire career has been games.\n\n\n\nI taught myself to program because I played Lunar Lander on an HP65 and\nit hooked me.\n\nThe first several programs I wrote on the Apple II were all games, including\na first-person driving game (\"pedestrian invaders\") where your goal was to run \ndown pedestrians, an adventure / D&D inspired text-based adventure game\nwith inventory management and combat (I wrote a text parser without knowing\nwhat a text parser was, adventure on the Apple II didn't have inventory or\ncombat, and adventure on the DEC10 didn't have combat), and a naval simulation \nbased on the tabletop wargame Sea Strike.\n\nIn college I realized computers simply weren't up to running the kinds of games\nI really wanted to play. The best personal computer you could get had 128kB of\nmemory. You couldn't even represent a Squad Leader map board accurately with\nthat much memory.\n\nSo I designed tabletop games, starting with ForeSight, using a Macintosh. This\ngodly machine was too intimidating to program at first (Apple never got the \nhang of creating nice developer APIs, but Steve Jobs and the crew at NeXT did\nand then Apple acquired them.)\n\nUsing the Mac taught me to understand and respect usability at a gut level \nbefore I really understood it properly. Don Norman's \"The Design of Everyday Things\"\nand Bruce Tognazzini's \"Tog on Interface\" (now hard to get) were life-changing.\nThe first showed you how things go wrong, and Tog showed you how to get them\nright.\n\nI spent the next 35 years being a programmer—sorry, \"software engineer\"—almost\nalways working on user-facing software, and constantly encountering the same \nproblems in different and ever more complex forms, and finding certain \nprinciples that seemed obvious and simplifying became more and more valuable\nover time.\n\n- **DRY** don't repeat yourself\n- good programming is applied laziness (pure math is pure laziness!)\n- figure out how not to do the thing you hate doing\n- figure out how users can avoid doing things they hate doing\n- all sincere feedback, even if it's wrong, is valuable\n\n## Games\n\nThe first 3D engine that was both fun to work with and performant enough to\nship good games was, in my opinion, [Unity](https://unity3d.com). (Althought honorable mention should \ngo to the late Mark Sibly's [Blitz3D](https://en.wikipedia.org/wiki/Blitz_BASIC).) \n\nUnity started out quite expensive, but today it's become effectively free for\nanyone not making their living from it. The real cost is in API thrash. If \nyou're not a fulltime Unity coder it changes so fast that it feels like you're \nlearning everything from scratch constantly. And it's big and clunky and heavy.\n\nAnd the programming language is C# which I've tried to love, but I just don't.\nIt's everything I hate about Typescript but worse: not surprising given \nTypescript was designed by the same guy, trying to make JavasScript more like\nC# (and C# was basically Microsoft's take on Java).\n\nIf you want the last word in graphics, it's not quite there. That's Unreal.\n\nAnd if you want simplicity, it's definitely not there. Just configuring a\nsingle input surface is a pain.\n\nNow, there's Godot, which is free and open source and has its own half-assed\nlanguage. The last thing I want to do is learn a new DSL that's in the process\nof being replaced.\n\nAnd one thing no 3D game engine out there does is handle the different 3D use cases\ncleanly. E.g. a VR version of a game, a touch version, a console version, and\na desktop version tend to be complete different builds. If you're Rockstar that's\nfine—maybe even a Good Thing—but if you're me it sucks. I want to do as little\nwork as possible for the greatest result. *Applied laziness*.\n\n\"sex\n\nI like owning my stack, i.e. having as few dependencies as possible and the\nonces I have should be open source and generously licensed.\n\n## tosijs-3d\n\nBecause building user interfaces is my day-job, I've built powerful libraries\nto do this well. But they're focused on the DOM, and the DOM is an inconvenience\nin 3D and basically useless in VR. So that means building out three complete \nuser interfaces at minimum (computer, touch device, and goggles).\n\nSo when I started building out [tosijs-3d](https://3d.tosijs.net), I knew I had a lot of ugly UI work\nto do because I needed to solve three messy problems on the surface level\n(rendering UI in three different contexts), and four ugly problems on the\ninput side (keyboard + mouse/trackpad), gamepad, \"glass\" controls, and VR\ncontrollers. Just handling mouse/keyboard in Unity was a pain. The solution\nused in AAA games for many years was **Scaleform**, a Flash runtime that rendered\ninto a texture layer. This outlived Flash (I think it's still in use!) This \nis literally one of the most garbage tools ever made, being emulated by more\ngarbage to solve a problem *badly*.\n\nBut then I had an idea.\n\nWouldn't it be nice if we could just implement one user interface and it worked,\nas expected, everywhere? The developer only has to think about one world. The\nuser only has to think about one world.\n\nToday, even if you're just playing a game, you probably need to quit and restart\njust to switch from \"flat\" 3D to VR. And when you're testing you're switching \nfrom goggles to laptop in the debug loop for the simplest thing.\n\n`tosijs` already does what we want, but it's tied to the DOM. Or is it?\n\n1. tosijs works in the DOM and SVGs are basically an extension of the DOM\n2. tosijs already has an svgElements proxy\n3. SVGs can render into canvases really easily\n4. canvases can be turned into bitmap images and those can be turned into 3d textures really easily\n\nAnd on the input side, GTAV et al basically has the best controls around and almost\neveryone knows how to use them. Let's map the keyboard and XR controls through\nthe default gamepad mappings inspired by GTA. And let's provide a virtual \nSVG-powered gamepad you can just overlay on 3D surfaces in whole or part.\n\nAnd let's create our basic scene so it by default supports \"flat\" 3D and XR.\n\n\"tosijs\n\n> This shows the tosijs-3d user virtual table example, the SVG (DOM version) is\n> running side-by-side with the one embedded in the BabylonJS scene. Note how\n> it's a virtual list (so only the minimum necessary elements are in the DOM)\n> but state is maintained on items scrolled in and out of view.\n\nOne world, one interface. Want to fix a minor UI bug you found in VR, you can\nprobably fix it in the DOM and test it there without touching your goggles. And\nyou can play the game in \"flat\" 3D and just dip into VR when you feel like it.\n\nIn a browser.\n\nAnywhere.\n\n",format:"markdown",date:"2026-07-30T19:26:23.004Z",keywords:[],summary:"Building UI for 3D and VR is historically a fragmented nightmare, propped up by garbage legacy abstractions like Scaleform. tosijs-3d fixes this by stripping out the friction entirely.\n\nInstead of maintaining three separate UI layers, it leverages tosijs's existing SVG bindings, renders them to a canvas, and projects them directly as 3D textures. On the input side, everything—VR controllers, keyboards, touch glass—gets routed through a unified, GTA-style gamepad model.\n\nThe result is a single, deterministic interface. You build it once, debug your VR UI directly in the standard DOM, and stop tearing a headset on and off just to fix a basic bug. One world, zero friction.",author:"Tonio Loewald",_created:"2026-07-30T06:53:02.073Z",_modified:"2026-08-04T12:07:58.174Z",_path:"post/rd7xk3qqp020"},"post/path=how-i-learned-to-stop-worrying-and-love-web-1-0":{title:"How I learned to stop worrying and love Web 1.0",path:"how-i-learned-to-stop-worrying-and-love-web-1-0",content:"\"show\n\nOne of the things I've ranted about repeatedly in the past is how Google's web crawler basically wants the web to be a bunch of static HTML files in directories, as it was before javascript and Apache and all those other things made life complicated. Now, you can get Google to index dynamic web pages if they hydrate themselves *fast* but, just when I thought I had figured this all out, along come LLMs and they start consuming web pages entirely using curl. We're back to 1993.\n\nSo the question is, how do we deal with this without going mad?\n\nAnd then it struck me—having spent the last ten years building insanely fast dynamic web pages prepopulated with metadata and data that could render once and be SEO friendly, I'd also built a system for hydrating static HTML with code that fires exactly once.\n\nBasically, for each url create a simple web page with html content wrapped in a custom element, and a script tag…\n\n\"simple\n\n…where the content is what you want the crawler / LLM to see, and you're done. No server-side rendering beyond the simplest HTML, your code executes *once*, on the client.\n\nThe only downside is you *do need* to render the HTML for each page, once, at some point (and you may also need to render thumbnails, sigh). But every other system out there requires this and *so much more*. At least this way you have absolutely control over the HTML that the crawler / LLM sees. The doc system just does this automatically and instantly, but a more complex system (like this blog) might need to be *slightly* cleverer (basically route the url to a highly cacheable function call that just renders the HTML and you're done). \n\nIt's actually simpler than my earlier prefetch architecture, which itself was simpler than any server-side rendering strategies I've seen.\n\nI tested this approach in [tosijs-3d](https://3d.tosijs.net) since it has zero users so the stakes are low.\n\nBut, as I've been building more stuff it occurred to me I could completely replace the build and documentation systems for *all* my projects with a system that implements this new approach and eliminates a growing maintenance overhead at the same time. So the [new documentation system](https://ui.tosijs.net/doc-site-system/) is built into `tosijs-ui` (but it's tree-shaken out if you don't use it).\n\nAnyway, this worked really well and the entire approach is now a reusable component of [tosijs-ui](https://ui.tosijs.net/) and it's replaced the bespoke system used for [tosijs.net](https://tosijs.net/) as well. Again, just try **view source** on any page of any of these sites and see for yourself.\n\nAs a bonus it produces nicer urls, proper metadata for every page, hierarchical navigation, [even] faster load times, and pages will load and be fully navigable even with javascript disabled.",format:"markdown",date:"2026-06-20T19:53:23.109Z",keywords:[],summary:"After years of trying to fight the good fight to make dynamic pages SEO-friendly, LLMs using curl have basically forced me to embrace pre-rendered HTML. Turns out I accidentally already had the perfect architecture.",author:"Tonio Loewald",_created:"2026-06-20T19:34:52.141Z",_modified:"2026-06-20T21:12:26.726Z",_path:"post/0vm2hnx6rt00"},"post/path=oulus-bike-parking-problem":{title:"Oulu's Bicycle Parking Problem",path:"oulus-bike-parking-problem",content:"\"handlebar\n\n> My bike parked outside the Oulu Library yesterday. It was so hard to get my bike in close enough to the rack that I gave up and locked the frame and front wheel to it rather than my preferred frame and rear wheel.\n\nOulu is an incredibly bicycle-friendly city. You can ride almost anywhere on protected paths. Drivers give cyclists respect, and there are bike racks everywhere. But they are *very* badly designed.\n\nThe worst offender, in my opinion, are the new bike racks outside the newly renovated Oulu Library. These racks are often almost full and perform very badly when crowded (and, really, that's the important use case).\n\n\"avoiding\n\n> This is a little further away from the library. Notice how cyclists have avoided using the purpose-built bike racks and shackled their bikes to trees, which have these little cages that are much easier to secure a bike to than these poorly designed racks.\n\nThey're rectangular with sharp corners. Presumably this means they're *welded*. So in addition to having sharp corners to bang your head on, they are going to be prone to corrosion damage. This is next to the water, which is salty, in a city that is cold and windswept. What could go wrong?\n\nThey're also taller than most bikes' handlebars. They're taller than mine, for example. This means that to get a bike in next to another bike you often need to lift it and perform Tetris to get your bike in and out and to have it anywhere it can be securely locked.\n\n\"u\n\n> As far as I know the U-shaped bike rack is the most popular and common design in the world for a reason. If it needs a Finnish touch, just shape it like a Marimekko poppy and be done with it, but seriously fix the damn bike racks.\n\nConsider the boring option, used almost everywhere in the world. A U-shaped pipe, flipped over and embedded in the concrete or welded to a frame. Less metal, simpler, fewer or no welded joints, easy to get your bike next to and easy to secure your bike to. If one of these exists in Oulu I don't know where it is.\n\nOulu has a huge bike theft problem. And having badly designed bike racks is a gift to thieves and a pain for cyclists every day.\n\n\"not\n\n> This cyclist has used a very heavy and expensive Kryptonite chain but hasn't actually secured their bike at all. You could just lift the chain off and ride the bike away. The extremely elaborate and badly designed bike rack makes this error easy to make, and you can see a bike lock hanging off a rack in the background, the bike it \"secured\" long gone.\n\nFinns pride themselves on their sense of design. But sometimes I think they're more interested in the idea or aesthetics of design than actual good design. But—oh my goodness—I just spotted a set of U-shaped bike locks over near the Radisson Hotel. It's too far from the library but they're actually being used as intended and in preference to nearby trees.",format:"markdown",date:"2026-05-13T08:51:28.866Z",keywords:[],summary:"Oulu's a bike friendly city with a bicycle theft problem. Part of the problem in my opinion is actively terrible bike rack designs.",author:"Tonio Loewald",_created:"2026-05-13T08:52:07.062Z",_modified:"2026-05-13T12:23:00.265Z",_path:"post/ahi5oipr7an0"},"post/path=great-ideas-don-t-shake-the-world-they-wobble-it":{title:"Great Ideas don't shake the world, they wobble it",path:"great-ideas-don-t-shake-the-world-they-wobble-it",content:"\"an\n\nI was trying to find a logo for Xerox PARC or the Xerox Alto in SVG format for\nreasons I won't go into, and came across the really annoying diagram above\nin this [rather annoying article from the IEEE](https://spectrum.ieee.org/xerox-alto).\n\nThis is the \"great man\" theory of technology distilled into a diagram that firmly\nputs Xerox PARC in the middle. It's the \"Steve Jobs stole the GUI from Xerox\"\ntheory where \"Apple is just a marketing company that rounds rectangles\" theory\nof the world, popular with people who aren't engineers and only grudgingly used\nGUIs after spaces started appearing in file names and typing DOS commands got\ntoo tedious.\n\n## Which Came First: the Chicken or the DNA?\n\nYou could just as easily put HyperCard (off in a corner) in the middle or\nKernighan and Richie or the MIT Tech Railway Club or the Homebrew Computer\nClub in the center. Actually each of these would be *easier* to center.\n\n\n\nThis diagram forgets that:\n\n- the GUI was pioneered by Ivan Sutherland at MIT and Douglas Englebart and others at Berkeley and Stanford. But both got their idea from seeing *radar displays*. Realtime information displayed on cathode ray tubes. What an *idea!*\n- Dynabook came before PARC. \n- Alan Kay \"discovered\" OOP by reading the source of a Simula compiler (Simula being the first OOP language). \n- HTML was inspired by HyperCard and the web was implemented on NeXTStep. \n- Javascript was inspired by Lisp and either NewtonScript or ideas from NewtonScript. \n- HyperCard was inspired by Dynabook and both were inspired by Seldon's slate in Asimov's *Foundation*. \n\nBut also lots of people had that idea but it took a genius forged in the fire of the\nMac team to *implement* HyperCard.\n\nThe diagram shows Alto -> GUI -> Star -> Lisa -> Macintosh, whereas three of the \nfirst four were useless failed objects that didn't work very well, and the GUI\nthat Xerox PARC used was *awful*—Alan Kay famously said the \"The Mac is the first \npersonal computer good enough to be criticized\".\n\nThen we get into outright delusion. \n\n> ethernet -> PARC Universal Packet -> TCP/IP -> Internet -> Cloud.\n\n\n\nThe internet came *before* any of the things in this chain. And the cloud *is* \nthe internet. It's just branding. Before 99% (or more) of people had *ever seen* \nan ethernet cable, offices with Macs were networked via phone cables and people\nwere surfing the web using modems. When Apple stopped supporting AppleTalk\nand switched to TCP/IP it was a major quality of life drop for users, partially\nmitigated by Bonjour. (Shout out to Stuart Cheshire—it's been a while, I doubt\nyou remember me!)\n\nAt some point you could probably download porn in Sydney via modem from \nftp.stanford.edu where the only ethernet involved was on the Stanford campus,\nbut TCP/IP was running over your phone lines. \nUntil the early 2000s when smart routers that could assign IP addresses via \nDHCP became common, most small offices couldn't use ethernet.\n\nMany of these things in this diagram were *highly* influential and caused \nripples that reinforced each other at different points to form other great \nthings. Many of the things in this diagram don't even deserve to be in this \ndiagram. Star, Lisa, and Alto could all be omitted. \n\nThe fact that Apple built something a lot like the Mac that was super expensive and no-one \nused isn't news. Almost every great affordable product has at least one\nnot-quite-great, not-quite-affordable predecessor that came out earlier. If \nyou're lucky, that not-quite-great product didn't tank the company and it \ngets to try again.\n\nI'd put SRI—Englebart's Lab—where Alto is, and maybe several other major sources of ripples \naround it. MIT's Tech Railway Club, Bay Area Homebrew Computer Club. \n\nSome things are so profoundly influential it's super hard to decide:\n\n- Assembler? \n- COBOL? \n- Macro Assembler? \n- K&R? \n- LLVM?\n- Lisp?\n\nWe went from flipping switches to punch cards to binary assembler\nto macro assembler to compiled languages to compiler ecosystems to virtual\nmachines to a virtual virtual machine. And on the way there were interpreters\nand just-in-time compilers. Metaprogramming. Containers. Every step was \n*monumental* and *incremental*. \n\nThanks to Hank Green, I'm reading [The Fabric of Civilization](https://www.vpostrel.com/the-fabric-of-civilization) right now and the author points out that Jaquard (sorry, tangent cards -> programming -> babbage -> lovelace -> turing but don't forget Gödel) didn't even invent his card-controlled loom so much as refine earlier attempts to do the same thing, and then traces it back to elaborate systems of encoding patterns in threads used to pull warps and so on.\n\nYou learn to weave and out drops arithmetic and the theory of prime numbers and pretty soon a rope woven of wires and magnets is controlling the spacecraft that gets you into lunar orbit.\n\n## Ideas vs. Products\n\nOne of the many problems with this diagram is it confuses product space with\nidea space. GUI is an idea. Compiling is an idea. Multiplexing is an idea. Ideas\nare in the wind. The key is to recognize them, understand them, and keep them\nin mind for when they *can* and *should* be *applied*. \n\nA smart notepad that remembers what\nyou write on is a great *idea*. A computer so simple children can use it\nis a great *idea* (Alan Kay famously had kids play with his prototypes).\nComputers that can fit on post it notes and use the same OS as your smart\nwhiteboards are a great *idea*. A Scientific American article of Xerox PARC\nwaxed rhapsodic about all these ideas in the late 1970s. I read it. Thousands\nof kids like me probably did as well.\n\nThe whole problem with John Sculley's \"Knowledge Navigator\" video is that is \nwas a bunch of great *ideas*. What he shipped was a bunch of expensive \n*products* that didn't *work*.\n\nA *product* is also a bunch of ideas. Ideas about how to make other ideas work.\nIdeas about what makes ideas work. Ideas about which ideas are good or bad in\nwhich situations. It takes these ideas and gives them to the people who tried\nto build the product and the people who tried to use the product.\n\nRipples.\n\n## Missing the Ideas\n\nArthur C. Clarke was a real physicist who worked on radar, proposed using\nsatellites for communication. \n\nBut he *totally* missed the idea of miniaturization. As\nlate as 1960 he wrote *Into the Comet* in which it's assumed that once a\nspaceship's single, giant computer fails the astronauts will be reduced to\nusing abacuses. \n\nUnsurprisingly, Clarke's proposed communications satellites were expected to\nbe enormous and staffed by people, presumably with typewriters and filing\ncabinets.\n\nThe first transistor radio came on the market in 1954, and\nin 1951, Asimov made the *Foundation*'s core technical advantage miniaturization \nborne of resource scarcity. So Clarke missed both developments in the real\nworld and in the writing of his most famous contemporary.\n\n## Building Great Products\n\nSo, the fact Steve Jobs and his team got to see Xerox's GUI after, probably \nlike a lot of us, finding out about Xerox PARC from press coverage (the only \nthing Xerox PARC really contributed to Xerox) when Xerox was trying to invest \nin Apple hardly means he hadn't been exposed to all those ideas. What he saw was \nthat Xerox had come closer to embodying those ideas in a product than they\nhad realized. They went back to their offices and started considering how\n*they* could use those ideas to make a *product*. And one thing they realized\nwas Xerox PARC's software was insanely inefficient. They did the math and\ncould figure out that if they ignored most of the stuff in the middle of\nthat diagram, they could ship something people could actually (a) afford and\n(b) use.\n\nXerox was drawing icons on the screen using Smalltalk. If you ever used \nSmalltalk for anything, it was incredibly slow and a huge resource hog. \nThe most powerful PC laptops I ever saw in the 1990s were in the hands of IBM\nSmalltalk developers who needed them to run Smalltalk demos. \n\nA mac drew icons on the screen using hand-coded assembler. A great *idea* meets \ngreat *implementation* and becomes a great *product*. It also met several\nother great ideas: Steve Jobs had famously encountered calligraphy and\ntypography in college. Steve Jobs was obsessed with minimalism and simplicity.\nThe Mac had a one button mouse that was simpler, easier to use, and more\nreliable than Xerox's three button mouse. Apple's engineers had read Knuth and\nBresenham's line algorithm.\n\nAnd, in particular, Bill Atkinson decided that the graphics coordinate system\nshould pass *between pixel boundaries*. This *idea* meant that, decades later,\nscalable fonts and retina graphics *just worked* on the Mac, not so much on\nother systems.\n\nIf the Mac hadn't been implemented by a bunch of geniuses who collectively \nmerged a bunch of great ideas they had previously encountered, digested, and\nunderstood deeply enough to know when to apply them, there's no Mac and no-one\nwould give the faintest shit about the Alto. But if the Alto didn't exist, it\nseems pretty likely that someone using a well-made, well-designed, efficient\npersonal computer that \"just worked\" would have eventually had a graphical\nuser interface. Graphical games were already in arcades. Bruce Tognazzini had\nalready helped build the *Apple Presents...* disk tutorial.\n\nIt's impossible to decide what should go in the center of a diagram like this,\nbecause great ideas (and products) have so many interacting effects on people.\nBut a failed product almost no-one used definitely *shouldn't* be centered like\nthis. The Alto isn't *undeservedly* forgotten, it's just forgotten.",format:"markdown",date:"2026-04-28T07:21:46.642Z",keywords:[],summary:"I was really annoyed by this article in IEEE Spectrum placing the Xerox Alto at the center of modern technology. Like any tech nerd, I know the trope that Xerox PARC's work is \"forgotten.\" In fact, the idea that PARC is forgotten is so common, I think it's actually remembered a little too well. The \"great man\" theory of technology is not a simple dichotomy between Steve Jobs being a genius and Steve Jobs being a thief.",author:"Tonio Loewald",_created:"2026-04-28T05:13:44.390Z",_modified:"2026-04-28T07:22:12.217Z",_path:"post/z76j9nkh3sze"},"post/path=tosijs-icon-system":{title:"the tosijs icon system",path:"tosijs-icon-system",content:"\"susan\n\n> [Susan Kare](https://en.wikipedia.org/wiki/Susan_Kare) designed the original Mac icons. Aside from switching to color in the early 90s and then using anti-aliasing this pretty much was how icons looked for 15 years (20 years in the Windows world).\n\nIcons have been a core part of graphical user interface design at least as far back as Xerox PARC.\n\nApple standardized on 32x32 px bitmap icons for most purposes (smaller icons were used for things like menus) and supported that via ICON and ICNS resources. You could have 32768 resources of a given type—this would actually be adequate for even most of today's applications—and you simply invoked them by resource type and id, or you defined a constant.\n\nSystem Resources had negative ids, so you could call system icons using their (negative) ids.\n\nAll of which is to say that Apple and NeXT knew how to deal with this stuff in an incredibly simple and robust way. But not so much the web stack. In any event, until custom fonts and [SVG](https://en.wikipedia.org/wiki/SVG) became widely supported, the constant increase of bitmap resolutions has kept us all on our toes.\n\n## SVGs and Resolution Independent Icons\n\nWith the widespread support of custom fonts and SVGs it became possible for icons to be resolution independent and typically quite compact. Custom fonts were particularly powerful because you could turn a glyph (character) in a font into a custom icon and then use CSS magic to insert that glyph by name into the DOM and voila, scalable, stylable icon. If you could insert an SVG into the DOM bare (i.e. not embedded in an `` tag) you could even style it, because SVG was supported directly by the browser.\n\nThis led to several rival approaches for inserting SVGs into the DOM:\n\n1. Custom Fonts (convert your SVGs into a font, use CSS / code magic to insert icons)\n2. Custom Builds (convert you SVGs into code, use code to render the SVGs in the DOM)\n3. Direct embedding (use SVGs as images)\n4. Complex approaches involving SVG ids and other stuff I don't understand\n\nThe most popular approaches were the first, exemplified by [iconmoon](https://icomoon.io) and second, widely used by different react libraries.\n\nOne thing all these approaches had in common is that they tended to work well for exactly one library. So MUI might have a great icon system but good luck using its icons with something else. And the second approach tended to mean using a specific build system as well. In my opinion the best build system is no build system and the second worst is someone else's build system. The absolute worst is a build system maintained by Facebook.\n\nSince I started building my own frameworks, the goal has always been to have a single source of truth for icons, no magic or convoluted tooling, and be able to quickly and easily add and replace icons, distribute them with components, and have no mess or fuss, but there have *always* been tradeoffs.\n\n**2017** — in b8rjs I used [icomoon.io](https://icomoon.io), which I still think is a solid choice for managing custom icon fonts. But…\n - color icons are flaky,\n - doesn't play well with others,\n - can't really distribute the icons with your components.\n - difficult to use icons in CSS `content`\n - impossible to use icons in CSS backgrounds\n\n**2021** — processing Icomoon selection.json files into icon-data and then generating SVGs dynamically\n from the data.\n - no fancy SVG effects, like gradients (goodness knows I experimented with converting CSS gradients to SVG gradients) and, most\n - **strokes** need to be converted to outlines\n - outlined strokes can't be styled the way strokes can\n - blocks use of popular icon libraries\n \n**2024** — ingesting SVGs directly, with a little cleanup, and then using a proxy to insert them into the DOM.\n - no build magic needed: `defineIcons({ myIcon: '', ... })`\n - smaller icon files, even though I'm now including more icons (including *all the current* feathericons)\n \nThis gets us to `tosijs-ui 1.4.x`. You dump all your svg icons in a directory, organize them how you like, put specific kinds of icons in subdirectories to indicate their properties (e.g. stroked icons are recognized if there's a `stroked` directory in their hierarchy), and it builds an icon data file tosijs-ui's `icons` proxy can use.\n\nBasically, `icons.lock()` renders an inline `` of a lock. It can be styled with CSS very easily. `` wraps that icon in a custom-element which will change the icon if you change its properties. So we have:\n\n- inline SVGs that can be styled by CSS (for buttons, etc.)\n- both stroked and filled icons (unlike font-based systems)\n- support for color icons (without requiring multiple glyphs perfectly aligned)\n- icons can be rendered as data urls, e.g. to insert into CSS… (the little `owl` logo rendered under blockquotes is an example)\n\nAnd also:\n\n- no build process magic needed (your icons are \"just javascript\", no special CSS files needed, no magic glyph mappings). Adding new, or overriding existing, icons is trivial.\n- if you want automatic custom builds, `make-icon-data.js` has no dependencies, finds icons and creates an `icon-data.ts` file.\n- icons are just regular SVGs, not a specialized subset.\n- the icons are highly optimized and compressible (the code is comparable in size to what you get with a compressed font built from the same icons, except icon fonts don't support strokes, gradients, etc.)\n\n## Icon Composition\n\nWith 1.5.0, tosijs-ui's icon system adds three major new features:\n\n- **icon composition**: stack, transform, and style icons via naming conventions (e.g. `lock50s75o$shield`)\n- **extensible rules system** for custom prefixes and overlays (e.g. `unLock`, `checkFile`)\n- **icon redirects** eliminate redundant SVG data (e.g. `chevronDown` → `chevronRight90r`)\n\n### Why?\n\nWhat if we could just tweak, arrange, animate, and compose icons on-the-fly?\n\n\"composite\n\nUsing similar syntax sugar to the way tosijs handles CSS variables…\n\n\"html\n\nSuddenly you can do more, with less, faster and more consistently with less code and less data…\n\n\"dynamic\n\n### The Pin Saga\n\n\"pin\n\nI needed a pin icon for column pinning in the data table. The only pin\nin the feather set is a map pin, so I created a push-pin icon. It's the icon \nabove (except I made it pink using new features of the icon system).\n\n> ![pin icon in action](https://firebasestorage.googleapis.com/v0/b/liquid-force-425209-g2.appspot.com/o/blog%2Fpin-icon-in-action?alt=media&token=f678b30a-48bb-4e69-9c9e-6530b9d88b16)\n>\n> Why did I need a pin? Because tosijs-ui's [virtual table component](https://ui.tosijs.net/?data-table.ts) \n> now lets you pin columns.\n\nBut immediately I also needed un-pin, pin-left, and pin-right — a lot\nof new icons for one feature. Of course I could flip the pin with CSS, but\nthis is a problem *everywhere, all the time*: every directional icon\nneeds 2–4 variants, every action needs a negation, every status needs\nan overlay, every entity needs to be created, deleted, and so on.\n\nWhy not fix it once and also eliminate the need to maintain trivial\nvariations on every icon?\n\nWhat if `flipHPin` horizontally flipped the pin?\n\n> That's so yesterday! Today it's `pin0f`. The icon system now fully leans into\n> tosijs's CSS syntax sugar, including directly using it to embed CSS variables\n> into icon changes, so `lock25o` is a lock icon with opacity 0.25, and `lock_brandColorS` \n> is a lock with `stroke: var(--brand-color)`.\n\n\"pin\n\nAnd once you have transforms and overlays, you have an icon *language*…\n\n\"Screenshot\n
\n\"cloud\n\nAnd the cost in code was offset by eliminating core icons that were simply transforms of other icons. E.g arrows and chevrons, rewind is just fast-forward flipped.\n\n## Hiding the DSL\n\nOMG another DSL? Well, yes but with three mitigations. \n\nFirst, you can alias a name to a transformed icon. So unPin can be mapped to pin0f\nand it works (almost) exactly like there was an `unPin` icon.\n\nSecond, we lean into tosijs's existing system for handling CSS variables and \ncolors, so it looks like existing tosijs code.\n\nE.g. in tosijs, `vars.buttonPadding_50` produces `calc(var(--button-padding) * -0.5)` and\n`vars.textColor50o` becomes a 50% transparent version of `var(--text-color)`.\nIn the icon system `lock50o` becomes a 50% transparent lock icon, and `lock_brandColorS`\nbecomes a lock with `stroke: var(--brand-color)`.\n\nFinally, there's a clue in `unPin`. The `un` is a custom rule that looks like this:\n\n```\n{\n prefix: 'un',\n apply: (baseName) => `slash25o$${baseName}75s75o`,\n}\n```\n\nAnd `spin` is a rule that looks like this:\n\n```\n{\n prefix: /^spin(_?\\d+)/,\n apply(baseName, match, parts) {\n const dps = (match as RegExpMatchArray)[1].replace('_', '-')\n const duration = 360 / Math.abs(parseFloat(dps))\n const direction = dps.startsWith('-') ? 'reverse' : 'normal'\n // Resolve baseName with suffixes intact — they apply to the icon\n const icon = resolveIcon(baseName, [])\n const el = icon as HTMLElement\n if (el.animate) {\n const base = el.style.transform || ''\n el.animate(\n [\n { transform: `${base} rotate(0deg)` },\n { transform: `${base} rotate(360deg)` },\n ],\n {\n duration: duration * 1000,\n iterations: Infinity,\n direction,\n }\n )\n }\n return wrapIcon(baseName, parts, icon)\n },\n},\n```\n\n> Why do we use [Web Animations](https://developer.mozilla.org/en-US/docs/Web/API/Web_Animations_API) and not\n> a simple CSS animation? First, you'll notice we compose the transform with the icon's existing\n> transform (vs. create a new animation for every icon based on, say, a hash of its existing transform)\n> but more importantly it works inside the shadowDOM.\n\nSo `spin90Loader` is a loader icon that spins (clockwise) at 90 degrees per second.\n\nThis lets you turn frequently used icon compositions into simple (or not so simple)\nrules that just become part of your icon vocabulary.\n\n## Conclusion\n\n\"icons\n\nBack in the early 2000s I was asked to design an icon system for an Avaya PABX project. \n\nWhat's a PABX? It's that thing that answers phone calls that isn't a human being but might, eventually, route you to one. You know, the robot that says \"para español, pulse dos\" and responds to the tones on your phone.\n\nThe goal was for every function in the system to have an icon representation. The number of functions was enormous so the goal was for the icons to constitute a \"language\" of \"verbs\" and \"nouns\" that could be composed in a consistent way.\n\n> Yes, the icons marked 16x16 actually had to be rendered at 16x16 and look good.\n\nThis was literally designed as a giant set of layers in an Illustrator document, loaded into Photoshop, and then each individual icon was configured as a specific set of layers and exported in multiple file sizes. Today, with this system, you could go straight from source vectors to the screen, and compose your icons (and fine tune them) on the fly.\n\nAnd with this system's custom modifiers you could also make ringing phones \"jiggle\", talking users \"speak\". The possibilities are endless. You even get things like if the icon for a menu item is `verbPhrase`, then you can make the icon for the cancel button `unVerbPhrase` and the button for deleting the new thing `deleteVerbPhrase` after, say, writing a custom rule that turns `delete` into a `trash` icon overlay, maybe scaled down and colored with your `var(--action-color)`.\n\nSo instead of my old Illustrator → (make paper notes) → Photoshop → (consult notes) → Export Images workflow, where if someone doesn't like the skin color of the user icon I need to rebuild everything.\n\nAnd if you're using one of the other icon systems out there, life hasn't really gotten much better in 25 years, you just probably don't need to switch apps or export multiple sizes, and if you do Illustrator and Photoshop are scriptable. \n\n![fontawesome shirt icons](https://firebasestorage.googleapis.com/v0/b/liquid-force-425209-g2.appspot.com/o/blog%2Ffontawesome-shirt-icons?alt=media&token=4008e403-6ef1-4cc6-bd94-f8ce75388fe0)\n\nI used to use [Font Awesome](https://fontawesome.com/search?q=shirt) as a source for icons. (I think I contributed to the kickstarter funding the previous major version.) It currently has 801 icons that match \"shirt\" (some of them are definitely not shirts). But what if I want an addShirt icon? What if I want it in my `var(--brand-color)`?\n\nNow we can create each element as an SVG, the name of the icon defines how it's built out of elements. `unLock`, `spin120Loader_brandColorS$downloadCloud`. If we change `--brand-color`, everything updates automatically. If we redesign the cloud, ditto.\n\nNo build magic needed.\n\n",format:"markdown",date:"2026-04-26T08:56:15.559Z",keywords:[],summary:"tosijs-ui's icon system has already solved all the problems every other icon system deals with (and few if any of which deal with completely) without needing build magic. Now it adds the ability to compose and tweak icons on-the-fly.",author:"Tonio Loewald",_created:"2026-04-26T08:57:19.378Z",_modified:"2026-04-26T11:59:42.805Z",_path:"post/0n7zm0jkoztv"}}