Sign in

Patrick Dubroy

@dubroy.com
1.6K followers 293 following 670 posts

Programmer & researcher, co-creator of ohmjs.org. 🇨🇦 🇩🇪 🇪🇺 Co-author of wasmgroundup.com — learn Wasm by building a simple compiler in JavaScript. Prev: CDG/HARC, Google, BumpTop

PostsRepliesMedia
Patrick Dubroy @dubroy.com · 21/08/2026
Been meaning to learn Wasm? Now's your chance to grab our book for CHEAP.
011
Patrick Dubroy @dubroy.com · 02/06/2026
If you're not subscribed to his newsletter, you're missing out: registerspill.thorstenball.com
registerspill.thorstenball.com
Register Spill | Thorsten Ball | Substack
Thoughts about software engineering I can't keep in my head. Too ephemeral for blog posts, too long for social media. It's the messages I'd send if you'd asked me what's on my mind. Click to read Reg...
010
Patrick Dubroy @dubroy.com · 02/06/2026
Another great post from @thorstenball.com: Building software is learning → registerspill.thorstenball.com/p/building-s...
registerspill.thorstenball.com
Building Software Is Learning
An internal note to the Amp team on feedback and shipping faster
172
Patrick Dubroy @dubroy.com · 26/05/2026
There is also patrickcollison.com/fast, and @thorstenball.com reminded me of this from nat.org
120
Patrick Dubroy @dubroy.com · 25/05/2026
Thanks! Yeah, I deliberately avoided talking about LLMs here. Like many of my blog posts, it's mostly "things I wish I'd learned sooner". So my implicit audience is people like me, who feel responsibility for all code they ship (LLM-authored or not).
010
Patrick Dubroy @dubroy.com · 25/05/2026
Tag yourself, I'm "future altered by doing request"
A diagram illustrating a request-response communication pattern between two services, labelled "Figure 2: Requests for Work"

It shows two white boxes, labelled "service A" and "service B" on a teal background. It clockwise order, it reads:

(service A) hopeful for the future…decides to issue request → request → (service B) minding own business → (service B) future altered by doing request → response → (service A) hopes fulfilled, the future is now
120
Patrick Dubroy @dubroy.com · 25/05/2026
New blog post: Fast is better than slow → dubroy.com/blog/fast-is...
About 10 years ago, I realized all the best programmers I had worked with had something in common: they were fast. By that I mean that they moved quickly: we’d discuss a problem and an hour or two later they’d already have a patch ready or a prototype to show off.

It took me a while, but eventually I realized: they weren’t fast because they were great programmers, they were great programmers because they were fast.

Think about it — if you’re fast, you get data more quickly. That helps you make better decisions, sooner. It also means you learn faster, and over longer periods it means you learn more. Being fast also means you can try out multiple approaches to a problem and pick the best one.

A lot of people push back on this because it sounds like hustle culture. But there are lots of ways to move faster that don’t involve working long hours. Jamie Brandon has written a pair of excellent posts on this: Speed matters and Moving faster. You should go and read those if you haven’t already.

I have a few suggestions of my own — things that are a bit more about the messy reality of working as a software engineer than they are about coding per se. And I’m slightly embarrassed to admit that, unlike Jamie, they took me more than a decade to learn.
5103
Patrick Dubroy @dubroy.com · 03/04/2026
"You don't know which afternoon of debugging was the one that taught you something fundamental about your data until three years later, when you're working on a completely different problem and the insight surfaces" ergosphere.blog/posts/the-ma...
ergosphere.blog
The machines are fine. I'm worried about us.
On AI agents, grunt work, and the part of science that isn't replaceable.
131
Patrick Dubroy @dubroy.com · 01/04/2026
TIL: Python signal handling → github.com/pdubroy/til/...
Python signal handling
I've written signal handlers in Python many times before, but never looked deeply into the exact mechanics until today.

Here's a simple example of a SIGINT handler:

import signal


def handler(signum, frame):
    print('Signal handler called with signal', signum)


signal.signal(signal.SIGINT, handler)
Some questions I had, along with the answers that I discovered:

Q: When does handler run, and how it is interleaved with whatever's happening on the main thread?

Answer:

A Python signal handler does not get executed inside the low-level (C) signal handler. Instead, the low-level signal handler sets a flag which tells the virtual machine to execute the corresponding Python signal handler at a later point (for example, at the next bytecode instruction).

and

Python signal handlers are always executed in the main Python thread of the main interpreter, even if the signal was received in another thread.

(From the doc for the signal module)

Q: Isn't this kind of scary? So the main thread can be preempted at an arbitrary point to run the signal handler?

Answer:

Unfortunately, yes! This also means:

Warning: Synchronization primitives such as threading.Lock should not be used within signal handlers. Doing so can lead to unexpected deadlocks.

So the restrictions are pretty similar to C — see the signal-safety(7) man page.Q: Ok, so what's the recommended pattern?

A: On option is to just assign to a global variable in the handler (shutdown_requested = True), and do the actual handling in your main loop or whatever. But in many cases you may need some way to park/wake the code in the main loop. In C you can use the self-pipe trick; Python has built-in support for this with signal.set_wakeup_fd().

How deadlock can occur
It's interesting to look at exactly how deadlock can occur. Here's the implementation of Event from cpython/Lib/threading.py:

    def __init__(self):
        self._cond = Condition(Lock())
        self._flag = False
    
    # ...
    
    def wait(self, timeout=None):
        with self._cond:
            signaled = self._flag
            if not signaled:
                signaled = self._cond.wait(timeout)
            return signaled
So the main thread could get preempted after acquiring the lock, but before the wait. Then suppose the signal handler tried to use the set() method on Event. Here's how it's defined:

    def set(self):
        with self._cond:
            self._flag = True
            self._cond.notify_all()
…so you'd have a self-deadlock when the main thread tried to acquire a lock again in the handler.
0110
Reposted by Patrick Dubroy
CodeTV @codetv.dev · 25/03/2026
Did you miss @dubroy.com teaching us about WebAssembly No worries! Check out our conversation on the Web Dev Podcast, then code along with us on Learn with Jason. WDP: codetv.dev/series/web-d... LWJ: codetv.dev/series/learn...
codetv.dev
WebAssembly is not as hard as it seems | The Web Dev Podcast Series 1
Its common to only hear Wasm talked about in contexts that seem… advanced. How can mere mortals like us hope to build with it? Patrick Dubroy talks about flattening the learning curve and putting…
051
Patrick Dubroy @dubroy.com · 24/03/2026
New post on the @ohmjs.org blog — Inside Ohm's PEG-to-Wasm compiler → ohmjs.org/blog/2026/03... v18 is now more than 50x faster for real-world grammars while using about 10% of the memory 🔥 …this post goes into the details of how it's built.
Bar chart comparing performance improvements across three technologies: JSON shows 15.6ms (v17) reduced to 0.35ms (v18), a 44.6x speed increase; LiquidHTML shows 2,250ms (JS) reduced to 36ms (Wasm), a 62.8x speed increase; and ES5 shows 3,308ms (JS) reduced to 55ms (Wasm), a 60.3x speed increase. Blue bars represent 'Before' measurements and green bars represent 'After' measurements
0104
Patrick Dubroy @dubroy.com · 23/03/2026
Love this — microui: A tiny, portable, immediate-mode UI library written in ANSI C → github.com/rxi/microui Only around 1100 SLoC (!)
github.com
GitHub - rxi/microui: A tiny immediate-mode UI library
A tiny immediate-mode UI library. Contribute to rxi/microui development by creating an account on GitHub.
091
Patrick Dubroy @dubroy.com · 19/03/2026
In 30 minutes!
021
Patrick Dubroy @dubroy.com · 18/03/2026
Tomorrow at 17:20 CET / 12:20 EDT / 9:20 PDT, I'll be livestreaming at twitch.tv/jlengstorf, teaching @jason.energy about WebAssembly!! I'll do my best to condense the best parts of our Wasm book (@wasmgroundup.com) into ~60 minutes. 😄
twitch.tv
Twitch
Twitch is the world
044
Patrick Dubroy @dubroy.com · 17/03/2026
Interesting theme that came up in a few talks (incl. mine) was flat/packed tree representations. Michael Vollmer (Univ. of Kent) has done tons of interesting work in this area: recurial.com
recurial.com
Michael Vollmer : Programming Languages, Computer Science
041
Patrick Dubroy @dubroy.com · 17/03/2026
Had a great time hanging out at MoreVMs today, thanks @stefan-marr.de for the invitation and for organizing! I believe my talk was recorded, so hopefully I can share soon.
121
Patrick Dubroy @dubroy.com · 13/03/2026
Ah, that was with @avibryant.com and @kevinlynagh.com and it was the interpreter for Fidget bytecode, which I believe you and I discussed a bit on Discord? I really need to write that up and publish it. 😬
020
Patrick Dubroy @dubroy.com · 13/03/2026
Looking forward to a couple of @ohmjs.org- and @wasmgroundup.com-related things next week: 1️⃣ Talking about PEG-to-#Wasm compilation in Ohm v18 at MoreVMs: 2026.programming-conference.org/home/MoreVMs... 2️⃣ Live-streaming with @jason.energy on Thursday at 17:30 CET, teaching him about WebAssembly.
2026.programming-conference.org
MoreVMs 2026 - MoreVMs'26 - ‹Programming› 2026
The 10th MoreVMs workshop aims to bring together industrial and academic programmers to discuss the design, implementation, and usage of modern languages and runtimes. This includes aspects such as re...
140
Patrick Dubroy @dubroy.com · 13/03/2026
One more time — I'm looking for new consulting clients. Some ways I can help: ∙ Fractional tech leadership (tackling "leadership debt" in small eng orgs) ∙ Full-stack, 0 to 1 projects ∙ Language design & impl (eg with @ohmjs.org) ∙ JavaScript/TypeScript perf (🔁 appreciated)
01115
Patrick Dubroy @dubroy.com · 10/03/2026
Also I finally put together a consulting/ page, for anyone who's interested in working with me. dubroy.com/consulting/ Did I mention I still have availability this year? 😇
Consulting
I do technical advising and freelance development for companies big and small.

Some recent examples:

Part-time advising for a small startup, doing regular 1-on-1s with the CTO and selected ICs. Advised on technical architecture and team issues, and helped them hire a Head of Engineering and their first Staff Engineer.
Worked with HCI pioneer Michel Beaudouin-Lafon and his research group to build a fully incremental processing pipeline for Asciidoc with Ohm.
For a research group investigating parametric CAD/CAE systems, implemented a GPU-based interpreter (in Rust and WGSL) for rendering implicit surfaces.
For engineering projects, I’m especially interested in work where I can combine my deep systems expertise with frontend development and UX work.
260
Patrick Dubroy @dubroy.com · 10/03/2026
What I'm up to now → dubroy.com/now/ ∙ Teaching another Scratch course ∙ Still have availability in 2026 for consulting ∙ Ohm v18 is now ~50x faster than v17 🔥 ∙ Invited talk at MoreVMs next week ∙ Live pair programming with @jason.energy next Thursday at ~17:30 CET!
Spring has sprung over here; I’m enjoying the sunshine.

I’m teaching another Scratch course at my kids’ Montessori school. It’s 90 minutes once a week for 5 weeks, and this time, the theme is “programming artificial life”:

Learn programming and create your own interactive digital creature. First, you’ll design your character (on paper or on the iPad). Then you learn how to give it behaviour in Scratch. Let it walk across the screen, search for food, decide when it needs to sleep. Turn your iPad into a virtual world!

The bee-like thing above is my creature, of course. It flaps its wings, gets happy when you feed it ants, and loves when you “pet” it with your cursor.

I still have availability for new consulting projects in 2026. I also finally put together a consulting page in case you’re curious how I could help you. I’m always happy to chat about potential projects, so feel free to get in touch.

Over the past few weeks, I’ve been finishing up the last little bits of WebAssembly support in Ohm, and officially announced the v18 beta. I’ve continued to make performance improvements, and am pretty psyched that it’s now about 50x faster on real-world grammars 🔥.

Speaking of Ohm, I’ve been invited to do a talk at the MoreVMs Workshop next week. So I’ve been spending time preparing that.

And I’ll be doing live pair programming with Jason Lengstorf on Learn with Jason on Thursday, March 19. You should tune in!
1100
Patrick Dubroy @dubroy.com · 07/03/2026
TIL: Multithreaded WebAssembly → github.com/pdubroy/til/... (corrected)
Just like in JavaScript, you can do shared memory multithreading in WebAssembly! I've long known this was possible, but until the other day, had never actually played with it myself, so I decided to put together a small, self-contained example.

(This is for Node, but it's pretty much the same in the browser.)Details
Structured cloning of WebAssembly.Module
Normally you'd instantiate a Wasm module with WebAssembly.instantiate, which gives you a module instance. Here, we use WebAssembly.compile, which gives us a WebAssembly.Module. This is a stateless object that is structured-cloneable, which allows it to be safely shared across realm boundaries.

Serialization (an implicit part of structured cloning) of WebAssembly modules is defined in §3 of the WebAssembly Web API, which says:

Engines should attempt to share/reuse internal compiled code when performing a structured serialization, although in corner cases like CPU upgrade or browser update, this might not be possible and full recompilation may be necessary.

Shared memory
WebAssembly.Memory also support structured cloning. When we pass shared: true, the buffer property is a SharedArrayBuffer:

The structured clone algorithm accepts SharedArrayBuffer objects and typed arrays mapped onto SharedArrayBuffer objects. In both cases, the SharedArrayBuffer object is transmitted to the receiver resulting in a new, private SharedArrayBuffer object in the receiving agent (just as for ArrayBuffer). However, the shared data block referenced by the two SharedArrayBuffer objects is the same data block, and a side effect to the block in one agent will eventually become visible in the other agent.

Atomic add
The last piece of the puzzle is the i32.atomic.rmw.add instruction used in the addId function:

  (func (export "add") (result i32)
    ;; mem[0] += workerId
    i32.const 0
    global.get 0
    i32.atomic.rmw.add))
This instruction is defined in the threads proposal (a Stage 4 proposal, so not finalized yet), which defines "a new shared linear memory type and some new operations for atomic memory access".

i32.atomic.rmw.add is equivalent to LOCK XADD on x86. As described in the threads proposal:
0101
Patrick Dubroy @dubroy.com · 07/03/2026
The playground is awesome! Btw you might want to change the CSS for the shortcuts…it resolving to Fire Code for me, which has ligatures for many of these things, which makes it confusing. Adding `font-variant-ligatures: none` seems to fix it.
110
Patrick Dubroy @dubroy.com · 05/03/2026
And @krismicinski.bsky.social's "Natural Deduction for IfArith" lecture is also great: www.youtube.com/watch?v=neCr...
youtube.com
L14: Natural Deduction for IfArith
YouTube video by Kristopher Micinski
110
Patrick Dubroy @dubroy.com · 05/03/2026
A while back, someone in the @wasmgroundup.com Discord asked about resources for learning the formal notation used in the WebAssembly spec. One I like is Jeremy Siek's "Crash Course on Notation in Programming Language Theory": siek.blogspot.com/2012/07/cras...
siek.blogspot.com
Crash Course on Notation in Programming Language Theory
This blog post is meant to help my friends get started in reading my other blog posts, that is, this post is a crash course on the notation ...
281
Patrick Dubroy @dubroy.com · 03/03/2026
TIL: Fibonacci hashing → github.com/pdubr...
It comes up rarely, but on a few projects I've wanted a dead simple hash table implementation. Most recently, it was for an experiment in the Ohm WebAssembly compiler. When I'm compiling a grammar, I assign each rule name a unique ID, but I wanted a fixed size cached (e.g. 8 or 32 items) keyed by rule ID.

I discovered Fibonacci hashing, aka "Knuth's muliplicative method":

So here's the idea: Let's say our hash table is 1024 slots large, and we want to map an arbitrarily large hash value into that range. The first thing we do is we map it using the above trick into the full 64 bit range of numbers. So we multiply the incoming hash value with 2^64/φ ≈ 11400714819323198485. (the number 11400714819323198486 is closer but we don't want multiples of two because that would throw away one bit) Multiplying with that number will overflow, but just as we wrapped around the circle in the flower example above, this will wrap around the whole 64 bit range in a nice pattern, giving us an even distribution across the whole range from 0 to 2^64. To illustrate, let's just look at the upper three bits. So we'll do this:

size_t fibonacci_hash_3_bits(size_t hash)
{
    return (hash * 11400714819323198485llu) >> 61;
}
All we have to do to get an arbitrary power of two range is to change the shift amount. So if my hash table is size 1024, then instead of just looking at the top 3 bits I want to look at the top 10 bits. So I shift by 54 instead of 61. Easy enough.

It turns out the Linux kernel has used this for ~6 years; here's the comment from include/linux/hash.h:

/*
 * This hash multiplies the input by a large odd number and takes the
 * high bits.  Since multiplication propagates changes to the most
 * significant end only, it is essential that the high bits of the
 * product be used for the hash value.
 *
 * Chuck Lever verified the effectiveness of this technique:
 * http://www.citi.umich.edu/techreports/reports/citi-tr-00-1.pdf
 *
 * Although a random odd number will do, it turns out that the golden
 * ratio phi = (sqrt(5)-1)/2, or its negative, has particularly nice
 * properties.  (See Knuth vol 3, section 6.4, exercise 9.)
 *
 * These are the negative, (1 - phi) = phi**2 = (3 - sqrt(5))/2,
 * which is very slightly easier to multiply by and makes no
 * difference to the hash distribution.
 */
#define GOLDEN_RATIO_32 0x61C88647
#define GOLDEN_RATIO_64 0x61C8864680B583EBull
Why would you use this?
(I may get some details of this explanation wrong, because hashing and hash table sizing are a surprisingly complex subject!)

If I understand correctly, it makes sense to use this if (a) you don't have access to a good hash function, and (b) you want power-of-two (not prime) table sizes; and/or (c) you want the bucket calculation operation to be as fast as possible. (A multiplication plus a shift is significantly faster than modulo/division.)
030
Patrick Dubroy @dubroy.com · 01/03/2026
I should also mention that these are just big, long, mega-notes in chronological order. So pretty easy to find things by visually scanning, and (rarely) searching.
020
Patrick Dubroy @dubroy.com · 01/03/2026
It's just a single note for all cooking/baking stuff. So I can do Cmd+F or just visually scan. I don't try that many new things (maybe a few times a month) so it's pretty easy to find.
010
Patrick Dubroy @dubroy.com · 01/03/2026
My 80/20, grug-brained personal productivity system: - Cool stuff: URLs, books, movies, etc. I want to remember. - Useful stuff: how/where/etc for things I do a few times a year. - Cooked: for cooking/baking: what recipe (URL or book), any adjustments I made, how it turned out.
Raycast Notes search interface, with three notes highlighted with blue rectangles: 'Useful stuff’ (preview shows “Replacement parts for Roborock…”), 'Cool stuff’, and 'Cooked’ (preview shows “2026-02-12: Made gyoza again…”).
3100
Patrick Dubroy @dubroy.com · 27/02/2026
Oh, thanks, didn't know about that! Would have been eligible already on GitHub stars
000
Patrick Dubroy @dubroy.com · 27/02/2026
Heh. Thanks! Hope this is a good thing :-)
010
Patrick Dubroy @dubroy.com · 27/02/2026
TIL that everyone who installs the Vercel CLI now gets a copy of @ohmjs.org
3191
Reposted by Patrick Dubroy
WebAssembly from the Ground Up @wasmgroundup.com · 26/02/2026
Is it the end of February already?? Yes, yes it is. Which means we just sent out our #Wasm tidbits. Sign up here to get it in your inbox once a month (ish): sendfox.com/wasmgroundup
Ahoy!

Hope your February has been swell. Here in southern Germany, it's been a relatively snowy winter…but it's finally starting to feel like spring.

First and foremost, we wanted to let you know that we published a new blog post last week, A WebAssembly interpreter (Part 2). In Part 1, we created a simple Wasm interpreter from scratch, but it was only able to evaluate expressions consisting of literals. In the latest post, we add support for local and global variables. Give it a look!

And here are your Wasm tidbits for February:
 • "WebCC is a lightweight, zero-dependency C++ toolchain and framework for building WebAssembly applications. It provides a direct, high-performance bridge between C++ and HTML5 APIs." And then there's Coi, "a modern, component-based language for building reactive web apps", which is built on WebCC. 
 • Marimo is an open-source reactive Python notebook; like Jupyter, but better in many ways (no hidden state, stored as pure Python files, …). And it also supports WebAssembly notebooks, powered by Pyodide; in other words, Wasm notebooks execute entirely in the browser, without a backend executing Python. 
 • Along the same lines: Pandoc for the People is a fully-featured GUI interface for Pandoc (probably the most-used Haskell program ever). It lets you run any kind of conversion that pandoc supports, without the documents ever leaving your computer. It's based on the recent Pandoc 3.9 release, which supports Wasm via the GHC wasm backend.
042
Patrick Dubroy @dubroy.com · 25/02/2026
Here's my first creature.
050
Patrick Dubroy @dubroy.com · 25/02/2026
Starting another Scratch course at my kids' (Montessori) school today. A bit different this time — the theme is "artificial life". Taking some inspiration from @shiffman.lol's natureofcode.com
Programmierung künstlichen Lebens in Scratch

Lerne Programmieren und erstelle dein eigenes, interaktives, digitales Wesen. Zuerst entwirfst du deine Figur (auf Papier oder auf dem iPad). Dann lernst du, wie du ihr in Scratch Verhalten gibst. Lass sie über den Bildschirm laufen, nach Futter suchen, entscheiden, wann sie schlafen muss. Dein iPad wird zu einer virtuellen Welt!

Programming Artificial Life in Scratch
Learn programming and create your own interactive digital creature. First, you design your character (on paper or on the iPad). Then you learn how to give it behaviour in Scratch. Let it walk across the screen, search for food, decide when it needs to sleep. Your iPad becomes a virtual world!
1130
Patrick Dubroy @dubroy.com · 25/02/2026
slowmo.dev by @seflless.bsky.social is pretty damn cool — "Slow down, pause, or speed up time of any web content." Here's how it works.
 * This library intercepts time at multiple levels to slow down (or speed up)
 * all animations on a web page.
 *
 * ## How it works:
 *
 * 1. **requestAnimationFrame patching**: We replace window.requestAnimationFrame
 *    with a wrapper that passes modified timestamps to callbacks. Time-based
 *    animations that use the timestamp parameter will automatically slow down.
 *
 * 2. **performance.now() patching**: We replace performance.now() to return
 *    virtual time. Libraries that use this for timing will be affected.
 *
 * 3. **Date.now() patching**: We replace Date.now() to return virtual epoch
 *    milliseconds. Libraries like Motion/Framer Motion use this for timing.
 *
 * 4. **setTimeout/setInterval patching**: We scale delays by inverse of speed
 *    so timed callbacks fire at the expected virtual time.
 *
 * 5. **Web Animations API**: We poll document.getAnimations() and modify the
 *    playbackRate of all Animation objects. This affects CSS animations,
 *    CSS transitions, and element.animate() calls.
 *
 * 6. **Media elements**: We set playbackRate on video/audio elements.
 *
 * ## Limitations:
 *
 * - Frame-based animations (that increment by a fixed amount per frame without
 *   using timestamps) cannot be smoothly slowed down.
 *
 * - Animations created by libraries that cache their own time references
 *   before we patch may not be affected. The Chrome extension runs at
 *   document_start to minimize this issue.
070
Patrick Dubroy @dubroy.com · 24/02/2026
You can see it here: github.com/wasmgroundup... Ended up using mostly branded types, as that seemed to provide the best ergonomics.
github.com
011
Reposted by Patrick Dubroy
a ton of crates @tonofcrates.bsky.social · 24/02/2026
people often don't appreciate how dramatically the intensity of knowledge work, especially in STEM, has changed in the last 60 years even prior to the advent of AI. tough to imagine that a scientist would spend most of their time literally plotting data
69113
Patrick Dubroy @dubroy.com · 24/02/2026
TIL: Set difference vs symmetric difference → github.com/pdubr...
I just spent an annoying 30 minutes debugging an issue caused by a silly mistake —

I wanted to verify that two sets had the same contents. So I wrote something like this:

assert(newSet.difference(oldSet).size === 0, 'sets are different!')
But this doesn't detect if oldSet has some items that aren't in newSet! What I should have been using was symmetricDifference:

assert(newSet.symmetricDifference(oldSet).size === 0, 'sets are different!')
Both of these methods are Baseline 2024 features.

On the names
Swift's name for difference is subtracting, which less confusing imo.

I decided to see where the naming was discussed on the original TC39 proposal, and found tc39/proposal-set-methods#7, with a the following comment from tabatkins

Sorry for this being back-and-forth, but difference has the same lack of implicit ordering as complement did — it's not immediately, intuitively clear which element's values are retained. minus and subtract are both good; removeAll, while it implies mutation semantics, is also extremely clear and good in this regard.

Related: "symmetricDifference" vastly exceeds my design instincts for what is an allowable level of spelling difficulty in an API. "symmetric" is not an easy word to spell (my fingers just now tried to type it with a single "m"!), and combined with another 10 letters after, it's huge and terrible. xor has a non-obvious meaning for many people, including native English speakers, but it's short and easy to spell; unsure if it's good enough or not.

Interestingly, the conclusion in that thread was to use except. There was further discussion in #24: Method names should pick a theme, but I couldn't figure out where (or why) they decided on difference.
120
Patrick Dubroy @dubroy.com · 23/02/2026
Finally! 😅
180
Patrick Dubroy @dubroy.com · 23/02/2026
TIL: api-extractor → github.com/pdubroy/til/...
The new version of ohm-js (v18), is now in beta. It's written in TypeScript, whereas v17 was written in JavaScript with manually-updated type definitions.

I was looking for a way to make sure that I don't accidentally make changes to the API, which led me to api-extractor:

API Extractor is a TypeScript analysis tool that produces three different output types:

API Report - API Extractor can trace all exports from your project's main entry point and generate a report to be used as the basis for an API review workflow.

.d.ts Rollups - Similar to how Webpack can "roll up" all your JavaScript files into a single bundle for distribution, API Extractor can roll up your TypeScript declarations into a single .d.ts file.

API Documentation - API Extractor can generate a "doc model" JSON file for each of your projects. This JSON file contains the extracted type signatures and doc comments. The api-documenter companion tool can use these files to generate an API reference website, or you can use them as inputs for a custom documentation pipeline.

How I'm using it
In each package, I have an api-extractor.json like this:

{
  "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
  "mainEntryPointFilePath": "./dist/index.d.ts",
  "apiReport": {
    "enabled": true,
    "reportFolder": "./"
  },
  "newlineKind": "lf",
  "dtsRollup": {
    "enabled": false
  },
  "docModel": {
    "enabled": false
  },
  "messages": {
    "extractorMessageReporting": {
      "ae-missing-release-tag": { "logLevel": "none" },
      "ae-forgotten-export": { "logLevel": "none" }
    }
  }
}
Note that I'm only using API reports for now — no .d.ts rollup or documentation. (I'm using tsdown as well, which already bundles my types into a single .d.ts.)Then, in my package.json, I have a package script named api-report:

{
  "name": "@ohm-js/compiler",
  "version": "18.0.0-beta.8",
  // ...
  "scripts": {
    "api-report": "api-extractor run",
    // ...
  },
  // ...
}
This script runs in CI. When it's run, if the API report has changed, I get an error like this:

> @ohm-js/compiler@18.0.0-beta.8 api-report /Users/pdubroy/dev/ohmjs/ohm/packages/compiler
> api-extractor run


api-extractor 7.56.3  - https://api-extractor.com/

Using configuration from ./api-extractor.json
Analysis will use the bundled TypeScript version 5.8.2
Warning: You have changed the API signature for this project. Please copy the file "temp/compiler.api.md" to "compiler.api.md", or perform a local build (which does this automatically). See the Git repo documentation for more info.

API Extractor completed with warnings
 ELIFECYCLE  Command failed with exit code 1
191
Patrick Dubroy @dubroy.com · 20/02/2026
Looking forward to this — I'll be doing an invited talk at MoreVMs in Munich on March 17. Hope to see some of you there!
020
Patrick Dubroy @dubroy.com · 20/02/2026
So excited to officially released the beta version! It's been a lot of work over the past ~year.
0131
Reposted by Patrick Dubroy
WebAssembly from the Ground Up @wasmgroundup.com · 20/02/2026
Happy Friday! We just published a new blog post — A WebAssembly Interpreter: Part 2 → wasmgroundup.com/blog/wasm-vm... In the first post, we wrote a small #Wasm interpreter from scratch in JS. In Part 2, we extend the interpreter to support local and global variables.
wasmgroundup.com
A WebAssembly interpreter (Part 2)
Adding local and global variable support to our Wasm Interpreter
094
Patrick Dubroy @dubroy.com · 19/02/2026
I'm extremely hyped about the performance of the upcoming (#Wasm-based) @ohmjs.org v18. !!! (please don't let this be a mistake in my benchmarking)
Performance comparison tables showing WebAssembly significantly outperforming JavaScript. In the "After" state: JS matching takes 3267ms with 1090.7MB memory while Wasm matching takes only 146ms with 174.5MB memory (22.4x faster). Overall, JS total is 2291ms compared to Wasm total of 100ms (22.99x speedup). Wasm uses dramatically less memory: 6.88 MB heap vs 247.52 MB for JS.
0191
Reposted by Patrick Dubroy
‹Programming› Conference @programming-conf.bsky.social · 19/02/2026
Final 24h! Early bird for #prog26 ends tomorrow, Feb 20. Don't miss the art & science of programming in Munich (Mar 16–20)! Register: 2026.programming-conference.org Submit to the Substrates workshop: 2026.programming-conference.org/home/substra...
2026.programming-conference.org
‹Programming› 2026
The International Conference on the Art, Science, and Engineering of Programming—or ‹Programming› for short—is a new conference focused on programming topics including the experience of programming. ‹...
022
Patrick Dubroy @dubroy.com · 14/02/2026
This is exactly one of the findings of the study* on tabbed browsing I did ~15 yrs ago — tab usage is bimodal. Funny to see that it (anecdotally at least) still holds up! ∗ "A Study of Tabbed Browsing Among Mozilla Firefox Users" from CHI 2010: dl.acm.org/doi/pdf/10.1...
5435
Patrick Dubroy @dubroy.com · 14/02/2026
"This speed can lead users to race past useful experiences, particularly ones that are artistically or pedagogically helpful." Slowness, Politics, and Joy: Values That Guide Technology Choices in Creative Coding Classrooms → www.mcnutt.in/assets/tatto...
There is a natural tendency in designing interfaces to try to make
them as fast as possible, to rapidly, and seamlessly [36], automate
tasks that are not essential facets of the task at hand. Yet, this speed
can lead users to race past useful experiences, particularly ones
that are artistically or pedagogically helpful.
5.1.1 Slowing Things Down. Reducing the rate at which different
tasks can be performed gives space for both artistry and learning,
giving users time for reflection and personal growth.
Reflection. slowness seemed to have value for the production
of and critical engagement with art.
PTega suggested that “in the arts, there’s a real value to slowing down and taking the hood off things. Because it lets you ask
critical questions, [such as] if you’re truly engaging with it as a
medium”. PBaku argued for the value of integrating tedium into his
workflow, noting that it is useful to “integrate procedural ways of
thinking with more manual or repetitive or more tedious works”.
He went on to describe how a photographer friend intentionally
used an older and slower computer to guide the type of works he
could create. Emphasizing the importance of user agency in this
091
Patrick Dubroy @dubroy.com · 11/02/2026
"The root cause of the Spectre and Meltdown vulnerabilities was that processor architects were trying to build not just fast processors, but fast processors that expose the same abstract machine as a PDP-11." C Is Not a Low-level Language: spawn-queue.acm.org/doi/pdf/10.1...
A large letter 'C' on the left side with the title 'Is Not a Low-level Language' where 'Not' is emphasized in brown italics. Author name 'David Chisnall' appears below, with 'Your Computer Is Not A Fast PDP-11' in brown text at the bottom right.
151
Patrick Dubroy @dubroy.com · 08/02/2026
if you're on macos: github.com/lynaghk/vibe/
github.com
GitHub - lynaghk/vibe: Easy Linux virtual machine on MacOS to sandbox LLM agents.
Easy Linux virtual machine on MacOS to sandbox LLM agents. - lynaghk/vibe
160