Open source · Guest play · No tracking

Texas Hold'em, dealt on your server.
Or on ours.

No-Limit Hold'em is played in the browser by your group. A table is created, a link is shared, and the cards are dealt. The entire stack is started with a single docker compose up, and no request is ever sent beyond it.

On the managed instance a table and its link are kept for one day. Self-hosting is recommended where a longer history is wanted.

What is included

A complete cash game table

Everything that is needed for an evening of poker is provided, and nothing beyond it.

Genuine No-Limit rules

Side pots, short all-ins, minimum raise logic and odd chip splits are resolved on the server. Only the rendering is left to the client.

Ten seats, no sign-up

A link is opened, a name and an avatar are chosen, and a seat is taken. After a refresh or a dropped connection the same seat is restored.

Voice and video included

Audio and an optional camera tile are carried browser to browser over WebRTC. Only the handshake is relayed by the server, never the media itself.

Full host control

The table is administered by the browser that created it. Settings, blind schedules, pauses, seat removals, chip adjustments and chat moderation are all covered.

Hand log and replay

Every hand is recorded and can be replayed step by step. A leaderboard and the final standings are kept for each table separately.

Private by construction

Two containers and one SQLite file are deployed. No external request is made, and the client is served from your own instance as a local WebAssembly build.

Verification

The rules are checked by machine

A verifier is shipped with every release. Millions of hands are played against the live engine and every chip is counted.

200,000 tables played
4,343,686 hands dealt
0 rules broken in 207,338,793 checks
0 chips created or lost

Hand ranking

All 2,598,960 distinct five-card hands are ranked and counted. The nine category totals are matched against the textbook frequencies exactly.

All nine categories
CategoryCountedExpected
Straight flush4040
Four of a kind624624
Full house3,7443,744
Flush5,1085,108
Straight10,20010,200
Three of a kind54,91254,912
Two pair123,552123,552
One pair1,098,2401,098,240
High card1,302,5401,302,540

Shuffle

Cards are shuffled by Fisher and Yates from the cryptographic random source of the operating system, by the same code the live server deals with. A sample of 2,000,000 hands is tested against the theoretical distribution.

Testχ²p
Categories (8 df)8.790.360
Positions (51 df)58.570.218
How it is read

A p value far above zero means the deal cannot be distinguished from a perfectly uniform shuffle. In the second test each of the 52 cards is watched for appearing more often than it should.

Checked after every event

Random play is generated with all-ins, side pots, time-outs, rebuys, straddles and a moving button. After each single event eleven invariants are re-checked.

  • Chips in play are never changed during a hand
  • No card is ever dealt twice
  • Every pot is awarded to the best eligible hand
The other eight
  • Buy-ins minus cash-outs equal the chips on the table
  • Blinds and button are seated where the rules require
  • Only the player on turn is allowed to act
  • The offered fold, call and raise range is the legal one
  • Illegal bets are refused and no chips are moved
  • Main and side pots are matched by a second calculation
  • Split pots are equal and odd chips are passed clockwise from the button
  • Winnings and losses of each hand are cancelled out exactly
git clone https://github.com/argseby/Showdown.git
cd Showdown && make simulate

What is shown and what is not. The hand ranking is checked exhaustively, so no five-card hand has been left unseen. The rules and the chip arithmetic are checked over millions of random hands, which is evidence rather than a proof. Nothing at all is said about whether the cards are being looked at by whoever runs a server. For that reason Showdown is built to be self-hosted: once it is run by you, the deal is held in your own hands. Recorded run of 6 September 2026 over 1.2 trillion chips wagered, with a difference of exactly zero.

Player profiles

Profiles are offered, never required

Profiles are switched off by default. Where they have been enabled by an operator, they remain optional for every player at every table: a seat is taken as a guest with nothing more than a display name.

Switched off by default

The feature is governed by one setting, ACCOUNTS=true. While the default is kept, no sign-in is presented anywhere in the application and every table is played as a guest.

Guest play is never withdrawn

Where profiles are enabled, Play as guest is offered beside every sign-in. Seats, voice, video, chat, hand history and the table leaderboard are given to guests in full. Nothing at the table is reserved for signed-in players.

No mail, no third party

A profile is created from a name and a password. No address is requested, nothing is confirmed and no message is ever sent. One recovery code is displayed at sign-up and is the only route back in should the password be forgotten.

What is recorded

Hands played, winnings, the best hands made and the milestones reached are kept, and a seat is marked with the profile so it is recognised across tables. Nothing beyond that is collected.

Private until it is shared

Profile, winnings, best hands, awards and activity are held privately and are released one by one, to friends alone or to everyone. Anything that has been made public can be made private again at any moment.

Friends, only on request

A player is found by name or from their seat, and a friend's table is joined through its free seats. Invitations are delivered anywhere in the app and may be declined quietly or blocked for good.

How it is turned on

One line is added to .env and the stack is restarted. Nothing else is required, and guests are unaffected.

echo "ACCOUNTS=true" >> .env
docker compose up -d

What is not affected

  • Tables are still created and hosted without any profile
  • Display names are still chosen per table and are not treated as identities
  • No instance-wide leaderboard is kept, only one per table
  • On the managed instance profiles are left switched off, so every table there is played as a guest
Two ways to play

Tried in a minute, or run for good

Managed

play.showdown.kiunke.dev

The site is opened, a table is created and its link is shared. Nothing is installed and no profile is needed.

  • Available immediately
  • Served over HTTPS, so voice and video are permitted
  • Tables and their links are kept for one day and are then removed with their history
  • Capacity is shared and the number of open tables is limited
Open the managed instance

Self-hosted

Docker Compose

The same stack is run on your own machine or server, and your data is kept with you.

  • Tables, hands and chat are retained as long as you wish, 90 days by default
  • Your own domain, limits and optional player profiles
  • Supported on a home server, a VPS, Portainer or Dokploy
  • Docker is required, and HTTPS is required for voice and video
See the steps
Deployment

Self-hosted in three steps

Docker with the Compose plugin is all that is needed. The images are pulled from GitHub's registry.

The images are published on GitHub's registry, so nothing is compiled on your machine.

  1. The compose file is fetched

    mkdir showdown && cd showdown
    curl -fsSLO https://raw.githubusercontent.com/argseby/Showdown/main/docker-compose.yml
  2. Optionally, settings are adjusted

    Port, table limit, retention, player profiles, or STUN and TURN servers for voice and video across networks. A release is pinned with IMAGE_TAG.

    curl -fsSL https://raw.githubusercontent.com/argseby/Showdown/main/.env.example -o .env
  3. The stack is started

    docker compose up -d

http://localhost:8080 is opened, a table is created and the link is shared. In Portainer a repository stack is added with the compose path docker-compose.yml, and updates are performed by re-pulling the image and redeploying.

Stack A Go API and a Flutter web client are served behind Caddy, backed by one SQLite volume
Ports Only web is exposed, on 8080 by default. The API is kept internal
Updates docker compose pull && docker compose up -d. Migrations are applied at start

Configuration, backups and operations are documented in the README.

FAQ

Questions

Is a player profile required?

No. Profiles are switched off by default, and where they have been enabled by an operator they are still optional. A table is joined as a guest with a display name alone, and every part of the game is available that way. Guest play is not restricted, delayed or reduced in any manner.

What is gained by a profile?

Your seat is marked with it and your record is carried across tables: hands played, winnings, the best hands made and the milestones reached. Friends can be added, and a friend's table can be found and joined from a free seat. Every section is kept private until it is released to friends or to everyone.

Is an email address needed to create a profile?

No. A name and a password are all that is asked for. No address is collected, nothing is confirmed and no message is ever sent by the server. A single recovery code is displayed once at sign-up and is the only way back in if the password is forgotten, so it is best written down.

How long is a table kept on the managed instance?

One day. The table, its link, its hands and its chat are deleted afterwards. A new table is created for the next session, or history is preserved by self-hosting.

Is real money involved?

No. Chips are numbers on a table. No payment is taken, no wallet is provided and no exchange of any kind is supported.

By whom is a table managed?

By the browser in which it was created. An admin key is stored there and an Admin tab is shown. The key can be copied to another device, and the table can be managed by anyone holding it, so it should be shared with co-hosts only.

How is it known that the deal is fair?

Every release is put through a verifier by which millions of hands are played against the engine, with the rules and the chip count re-checked after every single event. On the last run 207 million checks were passed, no chip was created or lost, and the summary is shown above. The same run is reproduced locally with make simulate. Nothing is proven by it about the honesty of a server operator, which is precisely why self-hosting is offered.

Is anything sent home by the server?

Never. No outbound request is made by the API, and the client is served entirely from your own instance. A STUN or TURN server is contacted only where one has been configured by you.

Get started

A table is created in under a minute

No installation is needed to try it, and no sign-up is needed to play.