The tournament I talked myself out of
I built King's Bluff to settle my fantasy football draft order, and then I sat down with a calendar and did the math on running it for fourteen people. A bracket is thirteen matches. Even at fifteen minutes each that is most of an evening, and whoever loses in round one spends the rest of it watching.
The bigger problem was the shape of it. Head to head elimination means somebody knocks you out, and that somebody has a name and sits across from you at the draft. My league is friends and family. I wanted the draft order to be a good story, not a grudge.
So a different question: what could fourteen people play at the same time, in about five minutes each, where nobody plays against anyone in particular, and there is still a real decision underneath it?
One round, everybody at once
You get 1,000 points. There are fourteen draft slots. Split your points across them however you like, in secret, and the highest bid takes each slot from the top down.
That is the whole game, and it is a genuinely hard problem the first time you see it. Slot 1 is worth the most, so everyone wants it, so it is the most expensive thing to want. Win a slot and your night is over, which means every point you put on the slots below it is never read out. Bid too little everywhere and you land at the bottom by default. Bid everything on one slot and you are betting the whole draft on a number you picked without seeing anybody else's.
Nobody plays against a person. You play against the field, and you are wrong about the field in a way you cannot find out until everyone is in a room together. Ties break to whoever finished worst last season, which quietly hands the bottom of the table an edge and gives last year's champion a reason to bid the odd number.
You cannot spend points you do not have. The meter refuses the edit and shakes red rather than letting you overcommit and scolding you afterwards, which is the kind of thing you only think to build after watching somebody fill in a form wrong.
Making a spreadsheet feel like a game show
The bidding is a quiet week on your phone. The reveal is the whole point, and a table of numbers appearing on a television is not an event.
So slots resolve one at a time on the TV, cheapest bid first. Cards sit face down, the low ones flip and dim, and the last two shake under a pulsing glow before anyone knows which way it went. The winner's bid lands, then their trash talk goes up underneath it, then the slot drops onto the board and that manager is out of everything below.
The part I am happiest with is how it stays in sync. Nobody streams anything. The commissioner's tap writes exactly two things to the server, which slot is on stage and when it started, and every screen computes the same milestones from that one timestamp. A phone that joins halfway through a slot lands exactly where the room is, because it is doing the same arithmetic everyone else already did. Clock skew gets corrected against the server's own clock on every poll.
There is also a step mode that collapses the whole timeline to zero. One tap and the slot is simply true. It exists for anyone who has reduced motion turned on, and for the moment somebody says just show us the rest.
Getting fourteen people to actually bid
The design work nobody writes about is the part where fourteen adults with jobs have to do something by a deadline.
There are no accounts. A link is your identity, which means nothing to set up and nothing to forget, and it means the commissioner's job is handing out fourteen links. So the app sends them: a setup flow that takes a roster paste, an invite email per manager with their team and the rules on it, and a tracker showing who has opened theirs and who has not.
Then the first batch went out and people told me it looked like spam. They were right. It arrived from an address at a domain none of them had heard of, with a subject line that never said which league it was about. Two changes fixed it. The mail now signs itself with the league's name in front of the address, and the league leads the subject line. It is a small thing that decides whether any of the rest of this gets used.
The same problem in a different coat: the commissioner's link is the only way back into a league, and there is no password reset that is not also a way to steal somebody's draft. So the first card a new commissioner sees is one that mails the link to them, and it now leads with the email field rather than hiding it behind a button.
The morning after
The night has to leave something to argue about. So the board is two pages doing two different jobs. The first is the record.
Every bid anybody made, fourteen managers down and fourteen slots across, with a mint ring on the cell that won. You are not supposed to look up a number in it. You are supposed to see the shape of what everyone tried, which is legible from the back of a room in a way fourteen separate bid sheets never would be. One manager bid 71 on all fourteen slots and let the tiebreaker sort it out. Another put their entire budget on the last slot and nothing anywhere else. A third tapered from the top and ran out by slot eight.
The shading scales to the ninetieth percentile of bids rather than to the largest one. Scaling to the largest means a single person going all in flattens everybody else into the same shade of nothing, and the outlier is already obvious from the number printed in the cell.
The second page is the argument.
Two numbers per slot on one scale. The pale column is everything the league aimed at that slot. The bright one inside it is what it actually took to win. The gap between them is the story: a room can throw two thousand points at slot 1 and watch it go for a few hundred.
Hovering a column breaks it down by person, biggest bid first, with anybody who had already won a slot dimmed and tagged. That list is the second version. The first was a number labelled "still live," which I could not explain without first teaching the rule it encoded, and a label you have to teach is not a label. Showing who was actually in the running says the same thing and teaches the rule on the way past.
My favorite number on the page is the one I did not plan. Winning ends your night, so every point you bid below your own slot is sealed, counted, and never spoken out loud. In a test league it came to more than three times what the draft order actually cost.
Cheap enough to leave running
This runs for one weekend a year, so the whole build is bent toward costing nothing the rest of the time.
CharTaker and MealMapper both sit on a vendor SDK, which is fine until the free tier changes shape. This one talks to plain Postgres over a connection string and nothing else, so the database is a swappable part rather than a dependency. Local development runs against a JSON file behind the same interface, which also means the tests need no database at all. State is a single document per league, the schema migrates itself on first use, and screens poll rather than hold sockets open, at 700ms while the reveal is live and four seconds when nothing is happening.
There is a small daily cron whose only job is to touch the database so the free tier does not put it to sleep between August and September. Free hosting, free database, a domain, and the transactional email is inside its free tier at fourteen sends. The reused part is judgment about which corners are safe to cut, not the stack.
Where it is now
It is out with my league and the bids are open. Draft night is in September.
The part I was hoping for is already happening in the group chat. The bid sheet has a field for a statement that gets read out if you win your slot, so people are writing them for an audience, and the rest of it has turned into announcements about what everyone is supposedly putting on slot 1. Some of that is true. None of it is checkable. Everything stays sealed until the room is together, including from me, which is the one rule that makes the rest of it work.
That was always the point. Not a fair draft order, a fair draft order that gives everybody something to accuse each other of.
