Skip to content
← All work

River

Texas Hold'em for friends, where nobody has to trust the dealer.

A development render of River's Rooftop venue at dusk, eight black-tie players around the table. On hover, a hand plays out on the live 2D table at playriver.uk: bets go in around a full table and a raise is set on the arc.
Year
2026
Role
Design and engineering
Stack
TypeScript · Next.js · WebSockets · Supabase · PyTorch · Blender

A question this site keeps returning to, this time at a card table: how do you know a record is true? Proof-Lens asks it of a file, Melody of a filing, Revenue OS of a sent email. River asks it of a shuffle, and of every chip that moves.

This is an early v1, live since late September. What you can play today is the 2D table. The 3D venues, built in Blender, are in development, and the render above is one of them. The repository is private.

The problem

Online poker is either a mobile casino engineered to sell chips, or a console title that wants a full evening, a headset and a lobby queue. Neither lets a group of friends open one link and be dealt into the same hand.

And every online card game asks for the same leap of faith: that the server shuffled honestly. You never see the deck. You are told it was random.

The approach

The server owns everything, and proves what it can.

The bots

Empty seats fill with bots, so a group can always get a game. They have ordinary names, and nothing at the table says which is strong.

A bot sees only what its seat could legally know. It never sees another player's cards or the cards still to come, and the server turns every decision into a legal move. Those rules came first, before any attempt to make the bots good.

The first bots were rule-based, and lost heads-up to every test style by 76 to 101 big blinds per 100 hands. Each version since has been measured rather than judged by eye. The live champion, Nell, runs a CFR+ solver on every two-player river, solving from both players' ranges, then bends the solution towards what each opponent tends to do, trusting a read only as far as the evidence for it goes. Across twelve fresh tables that beat the previous version by 24.5 big blinds per 100 hands. Against Slumbot, a competition-grade heads-up bot, she comes out about break-even over 7,231 hands: 4.5 big blinds per 100 behind, with all-ins valued at their odds. The interval runs from 45 behind to 36 ahead, so she may still be losing, and the claim is break-even, not better.

Learning the turn

Nell solves only the river exactly, because nothing can solve a whole hand of poker while it is being played. DeepStack, one of the research bots that beat professional players, got around that with neural networks that estimate what the rest of a hand is worth, so the solver can stop early and ask. That is the research half of River.

I trained value networks in PyTorch on solver output, starting from 200,600 river spots solved overnight, and built a depth-limited turn solver that uses a network to value everything after the turn. The turn network's error fell from 5.7% to 3.3% of the pot, measured against turns solved exactly over every river card, once it learned what the betting adds rather than only what the cards are worth.

The turn solver made the bot harder to exploit: a best-response opponent that knows its strategy won 42% less from it. It still lost chips to Nell at the tables. So I instrumented every hand and built an explainer for each turn decision. One fault fell out quickly, a broken handover of ranges from the turn to the river. The rest traced to one hand: the bot folding jacks full. Re-solved with exact values it still folded, because a subgame solved on its own, against an opponent assumed to play perfectly, is unsafe. That is a known failure in the research literature, and naming it decided the next step instead of another round of tuning.

This part is research. The bot at the live tables does not use a network yet.

What was hard

Discovering that "provably fair" was a claim the first version could not back.

The original shuffle drew every hand in a room from one seed, chosen when the room was created, using a generator with about four billion possible states. A deck of 52 cards has far more orderings than that, so most hands could never be dealt at all. The commitment proved the server had not changed its mind. It proved nothing about how the seed was chosen. The rule that came out of it is in the decision log as provably fair, or do not claim it.

The other hard part was the characters. More than thirty passes in Blender took the first one from a stock suit to a tailored black-tie figure, but none produced a seated poker pose that held up, so a character's pose and fit are now made by hand. The venues themselves stay as code: geometry, light, cameras and props all live in a Python pipeline, and building is the only way a venue comes into existence.

What I'd change

Next is safe continual re-solving: solving each street again from where the hand actually stands, in a way that cannot be exploited the way the turn solver was, from the flop down, with networks for the flop and before it. It goes live only when it beats Nell head-to-head on the same deals.

And the 3D venues need to reach the live table. Until they do, the strongest visual work in River is something players cannot yet see.

Method

I design River, set the rules its work is held to, and verify what ships. AI coding agents do much of the implementation under that review, and the record of it, including what went wrong, is kept beside the code.

Zain Butt · London, UKEmailCVGitHubLinkedIn