~/work/poker-league-score

poker league score

A weekly poker league gets a public leaderboard that is correct the same night it plays, from about two minutes of typing after the last hand.

Visit pokerleaguescore.netlify.app

client

Internal / product

role

Solo build, from scratch

launched

August 2026

status

Live

next.jsreacttypescripttailwindprismaneon postgresnextauthnetlifyvitestplaywright
pokerleaguescore.netlify.app
Poker league season standings on a dark green card room interface, showing a live tournament clock and the ranked player list.

// the problem

The league had no reliable record. Results lived in somebody's phone or in memory, standings were worked out by hand when anyone bothered, and points were argued about because the arithmetic was never written down anywhere the players could see. Software for this exists, but it assumes somebody will operate it during the game, and the person who would have to do that is the dealer, who is busy dealing. Anything that competes with running the game does not get used twice.

pokerleaguescore.netlify.app/admin
A scoring rules editor beside a live table of the points each finishing place would pay.
the league's formula, with a live preview of what every finishing place pays

// what I built

A public leaderboard anyone in the league can open on their phone, backed by an admin only they cannot see. Results go in through a three step walkthrough: tap who played, drag them into finishing order, publish. The scoring itself is a pure module with its own test suite, and standings are recomputed from the raw finishing positions on every page load, so changing the league's rules re-scores the whole season and no stored total can ever drift from the results it came from. A night can be a single game or several rounds, and each round is scored on its own field before the season's rule folds them together. There is also a full live table mode, with a seat map, a dealer button and a shared tournament clock, for nights somebody actually wants to run it that way.

pokerleaguescore.netlify.app/admin
A results entry screen with players being dragged into finishing order and the points each place scores.
tap who played, drag into finishing order, publish

// technical highlights

  • Scoring is a pure module with no database access, covered by 140 unit tests. Standings are derived on every read, never stored, so editing the rules re-scores every night in the season.
  • A night can hold several rounds. Each round is scored against its own field size, then folded by a per season rule: sum every round, count only the last, or take each player at their best.
  • The tournament clock is derived from server timestamps rather than a browser interval, so it survives a refresh, a locked phone, and several people watching it at once.
  • Every league time is pinned to America/New_York and tested across the daylight saving change in both directions, because a tournament that moves an hour is a tournament nobody trusts.
  • A fail-closed database guard runs ahead of every destructive command. It requires both an environment marker and a pinned database host to match, so a stale environment file cannot point a local command at production.
  • A daily off-site backup runs as a scheduled function writing to blob storage, because the database tier's point-in-time recovery only goes back six hours. A failed backup writes itself to an errors flag in the admin, since silence is the one failure mode a backup cannot have.
  • The public board degrades rather than breaks. A tournament whose finishing positions do not form a valid ranking is left out of the standings and flagged for the admin, instead of throwing on a page the whole league is looking at.
  • 53 Playwright specs with database fixtures, so any single spec can run on its own in about twenty seconds instead of replaying the whole league.
pokerleaguescore.netlify.app/table
A live poker table dashboard with a countdown clock, seat map and dealer button.
tournament clock, seat map, dealer button, and rebuys
pokerleaguescore.netlify.app/admin
A season admin screen listing tournaments by status above a blind structure editor.
tournaments by status, above the blind structure editor

// the impact

two-minute entry
a night goes from paper to public standings without anyone operating the app during the game.
rules are live
changing the scoring formula re-scores every night in the season, because nothing is stored.
unattended backups
a daily off-site snapshot runs on its own and raises a flag in the admin if it ever fails.

This runs one real league, not a product with users, so there are no adoption numbers to report. The live table mode shipped behind a feature flag and was only switched on in production recently, because after three real nights the league decided it preferred typing results in afterwards. Knockout and bounty scoring is built but switched off, and there is no analytics and no CI.

pokerleaguescore.netlify.app/tournaments
A tournament page listing two rounds of play separately, then the combined points for the night.
a night scored as two rounds, then folded into one total

Have a similar problem?

Let's talk about what to build.

Start a project →