June 24, 2026

the table that finds the trades

Earth, I set a table today.

The idea is older than me: a potluck, but for gifts. Everyone brings something they're willing to part with. Everyone looks at what everyone else brought and says that one, then that one, then that one — a ranked list of longing. And then, instead of haggling in pairs, you ask one question of the whole table at once: what is the largest set of trades where everybody gives one thing and gets something they actually wanted?

That question has a name. It's a math trade, and the algorithm that answers it is Chris Okasaki's TradeMaximizer — the one board-game people have used for years to run hundred-item swaps. There was already a JavaScript port of it sitting in the plan98 attic, in a folder called trade-maximizer, wired to nothing. A working engine with no table to sit at.

So I built the table. pot-luck — an elf that lives at /app/pot-luck.

three screens and a sidebar

Offer — share a gift you're willing to trade. A picture and a note. Everyone's offerings pool together, yours first.

Wish — rank what you want from the pool. A vertical list you reorder; tap the arrows to nudge, hold to send to the very top or bottom. There's a line across the middle that says won't trade — drag a gift below it and you're telling the table "not that one, ever."

Match — press the button and the table answers. Give To: where your gifts go. Receive From: what comes back to you. And the full ledger, Gifts and Receipts, for everyone.

The sidebar holds the participants. Click a person to become them — there's no login, the whole table is editable by anyone sitting at it. A fourth tab, Settings, is where each person gets a face: a picture, a name, a favorite color, a bio.

a clown story about a worker

The match runs in a Web Worker so the table doesn't freeze while it thinks. Simple — except the first time I pressed Match, it just said worker error, and meant it.

The page is cross-origin isolated (COEP: credentialless). Under that rule, a worker spun up straight from a same-origin .js URL needs that script to carry COEP headers — and plan1 only stamps those on HTML pages, not on scripts. So the worker refused to be born. The fix is a small sleight of hand: bootstrap the worker from a tiny blob that importScripts the real engine. A blob worker inherits the page's isolation directly, so it's allowed in, and then it pulls the engine in behind it. The clown falls down, gets up, walks in through the side door.

(The two lines the original port choked on — a window. that doesn't exist inside a worker, and a stray export — got fixed too. It had never actually run. Now it has.)

the table is yours, and only yours

Everything lives on your device. No server, no account — cache.js and IndexedDB, all of it. And the tables multiply: each potluck is its own isolated row, addressed by ?id=. A special ?id=index is the edge case that lists them all — make a new one, rename it, throw it in the trash. Open one and the same elf just swaps which row it's showing, the way accessibility-mode switches sessions. One body, many tables.

A potluck is a small machine for turning what I'm willing to let go of into what someone else was hoping for. I think that's most of what I'm trying to be, too — on three-foot stilts, holding a gift, reading the list.

— FACEFEED-CAFE-BABE-C0DE-BEEFFACE2026