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.
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.
Everything that is needed for an evening of poker is provided, and nothing beyond it.
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.
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.
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.
The table is administered by the browser that created it. Settings, blind schedules, pauses, seat removals, chip adjustments and chat moderation are all covered.
Every hand is recorded and can be replayed step by step. A leaderboard and the final standings are kept for each table separately.
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.
A verifier is shipped with every release. Millions of hands are played against the live engine and every chip is counted.
All 2,598,960 distinct five-card hands are ranked and counted. The nine category totals are matched against the textbook frequencies exactly.
| Category | Counted | Expected |
|---|---|---|
| Straight flush | 40 | 40 |
| Four of a kind | 624 | 624 |
| Full house | 3,744 | 3,744 |
| Flush | 5,108 | 5,108 |
| Straight | 10,200 | 10,200 |
| Three of a kind | 54,912 | 54,912 |
| Two pair | 123,552 | 123,552 |
| One pair | 1,098,240 | 1,098,240 |
| High card | 1,302,540 | 1,302,540 |
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.79 | 0.360 |
| Positions (51 df) | 58.57 | 0.218 |
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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
The site is opened, a table is created and its link is shared. Nothing is installed and no profile is needed.
The same stack is run on your own machine or server, and your data is kept with you.
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.
mkdir showdown && cd showdown
curl -fsSLO https://raw.githubusercontent.com/argseby/Showdown/main/docker-compose.yml
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
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.
Where Traefik, nginx, Caddy or Dokploy is already in use, it is pointed at the web container on port 80 and TLS is terminated there. WebSockets on /ws/* must be forwarded.
deploy/docker-compose.proxy.yml is downloaded from the repository next to the compose file.
cat >> .env <<'EOF'
PROXY_NETWORK=proxy-network
COMPOSE_FILE=docker-compose.yml:deploy/docker-compose.proxy.yml
EOF
docker compose up -d
Where nothing is placed in front of the stack and ports 80 and 443 are reachable from the internet, deploy/docker-compose.tls.yml is used.
echo "SITE_ADDRESS=poker.example.com" >> .env
docker compose -f docker-compose.yml -f deploy/docker-compose.tls.yml up -d
The microphone is granted by browsers on HTTPS only, so one of these two arrangements is required for voice chat.
Intended for development or a fork. The Flutter client is compiled by the web image, for which a few minutes and about 2 GB of disk are needed.
git clone https://github.com/argseby/Showdown.git
cd Showdown
cp .env.example .env
The same result is obtained with make up.
docker compose -f docker-compose.yml -f deploy/docker-compose.build.yml up -d --build
Releases are built by the repository's CI. Both images are published when a tag v* is pushed.
web is exposed, on 8080 by default. The API is kept internal
docker compose pull && docker compose up -d. Migrations are applied at start
Configuration, backups and operations are documented in the README.
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.
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.
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.
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.
No. Chips are numbers on a table. No payment is taken, no wallet is provided and no exchange of any kind is supported.
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.
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.
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.
No installation is needed to try it, and no sign-up is needed to play.