A portfolio project by Georgi Markov

How this was built

A real-time No-Limit Hold'em table with play money only. The server deals every card: each table is its own Cloudflare Durable Object running a pure TypeScript rules engine, and chips move only through Postgres functions. Chips have no cash value and can never be cashed out. Chip packages are sold through Stripe Checkout in test mode, so no real money ever moves.

See the numbers

Architecture

Three services, each with one job. The browser only sends intents; the server decides what happens.

Browser

Nuxt 4 · Vue 3 · Pinia · Tailwind

  • HTTPS · pages + API

    Web app

    Vercel · Nuxt 4 / Nitro

    • Server-rendered pages in English and Bulgarian
    • The server API: session, lobby, Quick Play
    • Secret keys stay on the server
  • WSS · the game

    Game server

    Cloudflare Workers · Durable Objects

    • Checks the sign-in token (JWKS, ES256) and the page's origin
    • One Durable Object per table, with its own SQLite storage
    • Shuffles, deals, runs the turn timers and the AI bots
  • HTTPS · sign-in

    Data & sign-in

    Supabase

    • Sign-in: email, Google or a one-click guest
    • Postgres with row-level security on every table
    • Chips move only through Postgres functions, into an append-only ledger

Both servers reach Supabase with the service role. A table's money calls leave its Durable Object in one strictly ordered queue, so a late write can never create or destroy chips.

Engineering highlights

Measured, not estimated: every number comes from the project's test runs or its production check.

Rules engine

99.68%

Engine test coverage

Statements of the rules engine covered by its tests; the CI gate is 90%.

10,000

Simulated hands

Played on 2-, 6- and 9-seat tables, with an exact chip count checked after every action.

100,000

Deals checked against an oracle

The engine's own hand evaluator matches the pokersolver library on the hand category and on which hand wins.

p = 0.39

Shuffle fairness (χ² test)

Over 1,000,000 shuffles the test found no bias: no card favours any position (one run).

0.30 ms

Engine action, p99

99% of actions finish within this time over 2,000 driven hands; the budget is 1 ms.

AI bots

+815 BB/100

Hard bot vs Easy bot

Big blinds won per 100 hands over 5,000 mirrored hands (z = 9.78): both seatings get the same cards.

1.9 ms

Hard bot decision, p99

The slowest of the CI-gated table shapes (1.4–⁠1.9 ms); the budget is 5 ms per decision.

In production

0.86 ms

Server CPU per WebSocket message, median

Measured in production over a 10-hand session with 2 players (21.10.2026); the limit is 10 ms.

≈ 9.9

Storage writes per hand

Rows written per hand in that production session: half of the 20-row budget.

Tech stack

  • Nuxt 4
  • Vue 3
  • TypeScript
  • Pinia
  • Tailwind 4
  • Cloudflare Workers
  • Durable Objects
  • SQLite
  • Supabase
  • PostgreSQL
  • GSAP
  • Vitest
  • Playwright
  • Vercel

See it for yourself

Take a seat: AI players fill the empty seats and the first hand is dealt within seconds.

© 2026 Poker App · A portfolio project by Georgi Markov

The games are intended for an adult audience. The games do not offer real money gambling or an opportunity to win real money or prizes. Practice or success at social casino gaming does not imply future success at real money gambling.