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
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 · 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 · 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
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 · 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
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
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
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
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 · 05/02/2026
+1, really enjoyed this talk. Some incredible examples involving the "the sophisticated software at the very lowest layers of the stack".
0110
Patrick Dubroy @dubroy.com · 05/02/2026
I've been looking at patterns for specific "migration" and "compat" packages in the JS ecosystem. I know about: - react-router-dom-v5-compat - @ vue/compat - ember-compatibility-helpers - jQuery Migrate Any others I should look at? Bonus points if you've used them successfully.
351
Patrick Dubroy @dubroy.com · 05/02/2026
Don't miss this! It's a joy, and it feels like a throwback in the best way possible (caring about UI details, remember that??)
030
Patrick Dubroy @dubroy.com · 02/02/2026
What I'm doing now → dubroy.com/now/ Note: I have availability for consulting projects in 2026. If you know of something interesting, please get in touch!
Freshly baked pita breads cooling on a black wire rack on a kitchen counter, with a light blue kitchen towel on the left and fresh parsley on the right. The pitas are puffy and golden-brown with a light dusting of flour on their surfaces.
0142
Patrick Dubroy @dubroy.com · 31/01/2026
TIL: HTTP Range requests → github.com/pdubr...
Did you know that HTTP supports requests for a specific range of bytes? I didn't!

I first saw this mentioned a few weeks ago in the Protomaps documentation, and then it came up again in an episode of Developer Voices about DuckDB.

MDN: HTTP range requests:

An HTTP Range request asks the server to send parts of a resource back to a client. Range requests are useful for various clients, including media players that support random access, data tools that require only part of a large file, and download managers that let users pause and resume a download.

Here's an example:

curl https://i.imgur.com/z4d4kWk.jpg -i -H "Range: bytes=0-1023" --output -

…which issues the following request:

GET /z4d4kWk.jpg HTTP/2
Host: i.imgur.com
User-Agent: curl/8.7.1
Accept: */*
Range: bytes=0-1023
I was kind of surprised that I hadn't heard about this before, but I felt better when I found out that Kris hadn't either :-)
131
Patrick Dubroy @dubroy.com · 28/01/2026
New blog post — Look for what's true → dubroy.com/blog/look-fo...
A few times in my career I’ve gotten feedback that really knocked me on my ass.

The first few times it happened, I wasn’t mature enough to learn much from it. But eventually I discovered a little trick — something that helps me set my ego aside and actually listen.

The details aren’t all that important, but to set the scene: a Principal Engineer I worked with had called me out for being overly negative in a meeting. He pointed out that, as a senior IC, I should be mindful of how my actions could affect those around me.

In hindsight, he was spot on; but in the moment, it felt mostly wrong and totally unfair.

I spent the rest of the day in a miserable mood. In the evening, I was still stewing, and considering how to respond. I think I even started drafting a rebuttal email (I know, I know).
1171
Patrick Dubroy @dubroy.com · 25/01/2026
TIL: Tidy Data → github.com/pdubr...
On Friday I took the train from Munich to Brussels, and on the way there, converted my financial planning spreadsheet to a Python script using pandas. Now, on the way back home, I'm reading about siuba and dplyr, and I learned about Hadley Wickham's concept of Tidy Data:

Tidy data is a standard way of mapping the meaning of a dataset to its structure. A dataset is messy or tidy depending on how rows, columns and tables are matched up with observations, variables and types. In tidy data:

1. Each variable is a column; each column is a variable.
2. Each observation is a row; each row is an observation.
3. Each value is a cell; each cell is a single value.
This is Codd’s 3rd normal form, but with the constraints framed in statistical language, and the focus put on a single dataset rather than the many connected datasets common in relational databases.
180
Patrick Dubroy @dubroy.com · 21/01/2026
For those who might not be aware — "OpenAI co-founder and president Greg Brockman gave Trump’s super PAC a whopping $25 million" www.sfgate.com/tech/article... "US Army appoints Palantir, Meta, OpenAI execs as Lt. Colonels" thegrayzone.com/2025/06/18/p...
041
Reposted by Patrick Dubroy
🌫️ Grant Forrest @gfor.rest · 19/01/2026
Still thinking about worker vs boss mentality about AI.
grant.leaflet.pub
Little Bosses Everywhere
1124
Patrick Dubroy @dubroy.com · 19/01/2026
"I have been maintaining my site for over 30 years. It’s older than Wikipedia or Google. Most software doesn’t last that long, and I don’t expect sassc or esbuild to last as long as my web site. When I’m choosing tools I ask myself 'what happens when this software disappears?'"
1212
Patrick Dubroy @dubroy.com · 16/01/2026
New blog post: De-Googling → dubroy.com/blog/de-goog... …in which I tell you about some great services & companies that I'm very happy to have discovered.
Since early last year, I’ve been trying to reduce my dependency on the big US tech companies, and US companies in general. Following Tim Bray, I’ve been thinking of this as de-Googling.

I don’t really want to get into the reasons, except to say that I’m under no illusion that my support (or lack of it) makes any difference to those companies. For me, it’s much more about reducing risk1, and about supporting smaller companies whose interests are more aligned with mine.

Anyways! I wanted write about my progress here, in case it’s useful to anyone else who’s also considering this.

(For a more in-depth discussion of the various alternatives, I found European Alternatives and Getting off US tech: a guide to be quite helpful.)
44710
Patrick Dubroy @dubroy.com · 10/01/2026
Two talks on data-oriented design that I enjoyed this week: Data-Oriented Design and C++ by Mike Acton → www.youtube.com/watch?v=rX0I... (End with an unfortunately lame quote, but otherwise great) Cpu Caches and Why You Care by Scott Meyers → www.youtube.com/watch?v=WDIk...
youtube.com
CppCon 2014: Mike Acton "Data-Oriented Design and C++"
YouTube video by CppCon
020
Patrick Dubroy @dubroy.com · 10/01/2026
TIL: One-liners for downloading media → github.com/pdubroy/til/...
Two little tasks I had this week, which I learned some handy one-liners for —

(And I love that these are now one-liners thanks to uvx!)

gallery-dl for downloading GIFs from Twitter
For my Twitter archive, I wanted to download the original GIFs from a couple of my tweets. I used gallery-dl —

uvx gallery-dl 'https://twitter.com/someuser/status/123456789'
yt-dlp for downloading YouTube videos
Then, I wanted to watch a YouTube video (Cpu Caches and Why You Care by Scott Meyers) and for some reason the player wouldn't let me change the speed. For that I used yt-dlp:

uvx yt-dlp 'https://www.youtube.com/watch?v=WDIkqP4JbkE'
280
Patrick Dubroy @dubroy.com · 09/01/2026
Lots of gems here. Definitely worth a read if you blog or would like to. (And I'll be following @thorstenball.com's advice in 2026: set a schedule.)
082
Patrick Dubroy @dubroy.com · 09/01/2026
Finally have a self-hosted version of my personal Twitter archive, so I can really, truly delete everything over there. → dubroy.com/twitter It's actually a lot of fun going through these! And orders of magnitude faster and more pleasant than on the official site.
Patrick Dubroy's Twitter Archive page listing tweets by year from 2008-2025.Screenshot of a 'Popular' tweets section showing two posts. The first tweet from May 5, 2017 (3159 likes, 1428 retweets) comments on putting heads on geometric forms to aid mental rotations, with an embedded image of academic text about body-syntonicity in problem solving and the concept of 'kobitos' (little people). The second tweet is titled 'How to get VC funding' and shows a satirical recipe with bullet points about looking at complex realities, failing to understand them, attributing failure to irrationality, and creating an idealized vision.
1131
Patrick Dubroy @dubroy.com · 07/01/2026
"Valgrind is in essence a virtual machine using just-in-time compilation techniques." Huh. I had no idea. en.wikipedia.org/wiki/Valgrind
Valgrind is in essence a virtual machine using just-in-time compilation techniques, including dynamic recompilation. Nothing from the original program ever gets run directly on the host processor. Instead, Valgrind first translates the program into a temporary, simpler form called intermediate representation (IR), which is a processor-neutral, static single assignment form-based form. After the conversion, a tool (see below) is free to do whatever transformations it would like on the IR, before Valgrind translates the IR back into machine code and lets the host processor run it. Valgrind recompiles binary code to run on host and target (or simulated) CPUs of the same architecture. It also includes a GDB stub to allow debugging of the target program as it runs in Valgrind, with "monitor commands" that allow querying the Valgrind tool for various information.

A considerable amount of performance is lost in these transformations (and usually, the code the tool inserts); usually, code run with Valgrind and the "none" tool (which does nothing to the IR) runs at 20% to 25% of the speed of the normal program.[7][8]
040