{"id":50764879,"url":"https://github.com/narmod/pokerth-web-client","last_synced_at":"2026-06-11T13:00:35.521Z","repository":{"id":359666528,"uuid":"1242818307","full_name":"narmod/pokerth-web-client","owner":"narmod","description":"A modern web client for PokerTH, allowing players to connect to PokerTH servers directly from a browser through a WebSocket-to-TCP proxy.","archived":false,"fork":false,"pushed_at":"2026-06-05T22:43:04.000Z","size":12986,"stargazers_count":0,"open_issues_count":0,"forks_count":1,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-05T23:11:18.064Z","etag":null,"topics":["board-game","docker","family-game","nodejs","poker","pokerth","pwa","raspberry-pi","self-hosted","selfhosted","texas-holdem","websocket"],"latest_commit_sha":null,"homepage":"https://pokerth.ddns.net/","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/narmod.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"docs/SECURITY.md","support":null,"governance":null,"roadmap":"docs/ROADMAP.md","authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-18T19:34:36.000Z","updated_at":"2026-06-05T22:43:08.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/narmod/pokerth-web-client","commit_stats":null,"previous_names":["narmod/pokerth-web-client"],"tags_count":20,"template":false,"template_full_name":null,"purl":"pkg:github/narmod/pokerth-web-client","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/narmod%2Fpokerth-web-client","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/narmod%2Fpokerth-web-client/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/narmod%2Fpokerth-web-client/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/narmod%2Fpokerth-web-client/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/narmod","download_url":"https://codeload.github.com/narmod/pokerth-web-client/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/narmod%2Fpokerth-web-client/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34199516,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-11T02:00:06.485Z","response_time":57,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["board-game","docker","family-game","nodejs","poker","pokerth","pwa","raspberry-pi","self-hosted","selfhosted","texas-holdem","websocket"],"created_at":"2026-06-11T13:00:23.549Z","updated_at":"2026-06-11T13:00:35.513Z","avatar_url":"https://github.com/narmod.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# PokerTH Web Client\n\n\u003e A modern, mobile-friendly browser client for [PokerTH](https://github.com/pokerth/pokerth) — the legendary open-source Texas Hold'em poker game.\n\u003e\n\u003e **Load once, play anywhere:** cached as a PWA for instant launches, fully offline against bots, and ready to connect to any PokerTH server when you are.\n\n[![Publish Docker image](https://github.com/narmod/pokerth-web-client/actions/workflows/docker-publish.yml/badge.svg)](https://github.com/narmod/pokerth-web-client/actions/workflows/docker-publish.yml)\n[![Container image](https://img.shields.io/badge/ghcr.io-pokerth--web--client-2496ed?logo=docker\u0026logoColor=white)](https://github.com/narmod/pokerth-web-client/pkgs/container/pokerth-web-client)\n[![Raspberry Pi ready](https://img.shields.io/badge/Raspberry%20Pi-ready-c51a4a?logo=raspberrypi\u0026logoColor=white)](#raspberry-pi)\n[![License: AGPL v3](https://img.shields.io/badge/License-AGPL%20v3-blue.svg)](LICENSE)\n\n---\n\n## Contents\n\n\u003csub\u003e📂 = collapsible section — click the **“Show…”** line to expand it.\u003c/sub\u003e\n\n- [🎮 Live demo](#live-demo)\n- [Why this project exists](#why-this-project-exists)\n- [Screenshots](#screenshots)\n- [Features](#features)\n- [Login modes \u0026amp; transport](#login-modes-transport)\n- [Playing a game](#playing-a-game)\n- [Architecture](#architecture)\n- [Requirements](#requirements)\n- [Self-hosting](#self-hosting)\n  - [Quick install (one-liner)](#quick-install-one-liner) \u0026nbsp;📂\n  - [Docker](#docker) \u0026nbsp;📂\n  - [Manual installation (Ubuntu / Debian)](#manual-installation) \u0026nbsp;📂\n  - [Self-hosting on a Raspberry Pi](#raspberry-pi)\n- [Install the app](#install-the-app)\n- [Managing \u0026amp; resetting](#managing-resetting)\n  - [Managing the service](#managing-the-service)\n  - [The admin panel](#admin-panel)\n  - [Resetting the family leaderboard](#leaderboard-reset)\n- [Development (running from source)](#development) \u0026nbsp;📂\n- [Protocol notes](#protocol-notes)\n- [Known limitations](#known-limitations)\n- [Roadmap / Suggested next steps](#roadmap)\n- [License](#license)\n- [Acknowledgements](#acknowledgements)\n\n---\n\n\u003ca id=\"live-demo\"\u003e\u003c/a\u003e\n## 🎮 Live demo\n\n**Try it now: [https://pokerth.ddns.net/](https://pokerth.ddns.net/)**\n\nLeave the server selector on **LAN / Dedicated server** (the default) with **Guest mode** unchecked, choose any nickname, and play right away — no account, no install. The demo is hosted on a small VPS connected to a private PokerTH server, so feel free to create a table and invite friends.\n\nWant to try with **no server or connection at all**? Pick **🏋️ Training mode** and play instantly against bots — fully offline.\n\n\u003e Tip: it works just as well on mobile — add it to your home screen for a fullscreen app feel.\n\n---\n\n## Why this project exists\n\nI have been playing PokerTH for years and have a deep appreciation for the incredible work the PokerTH team has put into this game over so many years. **Thank you** to every contributor who built and maintained it. ❤️\n\nOne day I wanted to play a family LAN game with my wife and teach poker to my kids — on tablets and phones, without installing anything. The problem: **there is no official web client for PokerTH**. You need the native desktop app, which does not run on iOS or Android.\n\nSo I sat down and built one.\n\nIt started as a very simple interface — just enough to deal a hand around the table. But every family game brought new feedback (\"I can't tell the suits apart on my phone\", \"whose turn is it?\", \"can we have avatars?\"), and little by little those suggestions grew the bare-bones prototype into the much more complete client it is today.\n\nThis project is a **web frontend** that connects to any PokerTH server directly from the browser, with no app installation needed. It is designed to work great on phones and tablets so that family poker nights are just a URL away.\n\n---\n\n## Screenshots\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"docs/screenshots/08-desktop-game-light.png\" alt=\"In-game desktop view — table, theming, reactions and chat\" width=\"840\"/\u003e\n  \u003cbr/\u003e\n  \u003cem\u003eDesktop view — full table with the live appearance panel (deck, palette, felt, buttons, pucks, seats), emoji reactions, in-game chat with its emoji picker, the action bar with keyboard hints, and the hand log\u003c/em\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"docs/screenshots/01-connect-dark.png\" alt=\"Connect screen (dark theme)\" width=\"260\"/\u003e\n  \u003cbr/\u003e\n  \u003cem\u003eConnect screen — pick a login mode and join in seconds (light \u0026amp; dark themes included)\u003c/em\u003e\n\u003c/p\u003e\n\n\u003cdiv align=\"center\"\u003e\n\n\u003ctable\u003e\n  \u003ctr\u003e\n    \u003ctd align=\"center\"\u003e\u003cstrong\u003eCreate a table — light\u003c/strong\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003cstrong\u003eCreate a table — dark\u003c/strong\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd align=\"center\"\u003e\u003cimg src=\"docs/screenshots/04-create-light.png\" alt=\"Create a table (light theme)\" width=\"240\"/\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003cimg src=\"docs/screenshots/05-create-dark.png\" alt=\"Create a table (dark theme)\" width=\"240\"/\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd align=\"center\"\u003e\u003cstrong\u003eConnect — light theme\u003c/strong\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003cstrong\u003eTable menu \u0026amp; options\u003c/strong\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd align=\"center\"\u003e\u003cimg src=\"docs/screenshots/02-connect-light.png\" alt=\"Connect screen (light theme)\" width=\"240\"/\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003cimg src=\"docs/screenshots/06-menu.png\" alt=\"Table menu and options\" width=\"240\"/\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd align=\"center\"\u003e\u003cstrong\u003eLobby \u0026amp; chat\u003c/strong\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003cstrong\u003eAvatar picker\u003c/strong\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd align=\"center\"\u003e\u003cimg src=\"docs/screenshots/03-lobby-chat.png\" alt=\"Lobby and chat\" width=\"240\"/\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003cimg src=\"docs/screenshots/07-avatar-picker.png\" alt=\"Avatar picker\" width=\"240\"/\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\n\u003c/div\u003e\n\n---\n\n## Features\n\n### Connection\n- **3 server choices + a Guest-mode toggle**: pick **LAN / Dedicated server**, **pokerth.net**, or **🏋️ Training mode** (offline solo play against bots), then use the **Guest mode** checkbox (just above the Connect button, off by default) to switch the guest/registered variant of the two online choices. Internally the online choices still map to the four PokerTH login types; **Training mode runs entirely in the browser with no server, proxy, or connection**.\n- Optional authenticated login over TLS\n- TLS support (required for pokerth.net, optional for LAN). The TLS box auto-checks itself when you turn **Guest mode** off on pokerth.net (registered-account login).\n- Auto-fill of `host = pokerth.net` and `port = 7234` when **pokerth.net** is selected — the dedicated-server choice keeps the auto-detected hostname\n- Remember nickname / credentials via `localStorage`\n- Refresh button and fullscreen toggle on every screen\n\n### Lobby\n- Real-time table list with player counts, status badges, and each table's blind level and raise schedule\n- Table filters: **All / 🟢 Open / 🔓 No password / 👁 Watchable** (remembered across sessions)\n- **⚡ Join or Create** — one-tap auto-join or table creation\n- Advanced table creation: blinds, timeout (default 15 s), max players (default 5), **bot difficulty** (Easy / Mixed / Normal / Hard), **game-style presets** (🐢 Relaxed / ⚖️ Normal / ⚡ Fast), **blind-increase schedule** (every N hands or N minutes) with a raise mode (double / to a target / keep last), table speed (1–10), deal delay, **game type** (Normal / Registered-only / Invite-only), ranking on/off, spectators allowed/blocked, bots fill (with a min-humans-before-bots threshold), and an optional password\n- Spectator mode (👁 Watch)\n- Lobby chat\n\n### Poker table\n- Seats positioned according to server order, **locked after the first deal** (no mid-game layout jumps)\n- **Responsive seat layout** — on phones and tablets the seats tighten around the felt so players stay close to the table; desktop keeps the wider layout\n- **Table zoom** (desktop) — **+ / −** buttons by the table shrink the felt, community cards and opponents together (down to 60 %) or restore them up to the current size, while your own player bar and the action bar stay fixed; the level is remembered\n- Casino-style chip tokens: SB 🔵, BB 🔴, Dealer ⚫ gold — with `chipPop` animation\n- SVG arc timer around the active player's avatar + seconds badge below\n- **Card deal animation**: cards fly from the centre to each seat at the start of every hand\n- **Chip slide animation**: chips glide toward the pot on bet / call / raise\n- **3D flip animation** for community cards (flop × 3, turn, river)\n- Pre-flop hand-strength hint (Sklansky-Malmuth chart)\n- **Post-flop win probability** — Monte Carlo simulation against random opponent ranges\n- **Spades vs clubs visual distinction**: spades get a subtle blue tint so ♠ and ♣ never get confused on small screens\n- Pot strip showing hand number, total pot, and current betting round\n\n### In-game settings (⚙ menu)\nPer-player toggles, remembered in `localStorage` and applied instantly:\n- **BB / ¥ display** — show amounts as big blinds or as chips\n- **Assistance** — show or hide the pre-flop hand-strength + post-flop win-probability help above the action bar\n- **Quick-bet buttons** — 33 % / 50 % / 100 %-pot one-tap bets\n- **Auto-action button** — pre-commit fold / check / call before it is your turn\n- **Voice announcements** — spoken turn and action callouts (Web Speech API)\n- **Vibration** — haptic feedback on your turn (where supported)\n- **Sound on/off** (🔊) — mute or unmute all sound effects\n\n### Themes \u0026 customization\nA full appearance system, reached from the **Theme** button — pick a one-tap preset or fine-tune every axis yourself:\n- **Presets** (one tap): **PokerTH Dark** (the default, reproducing the official client's look), **PokerTH Light**, and **Green Casino** — plus gallery themes (Midnight Blue, Graphite, Royal Purple, Sleek)\n- **Customize** — six independent axes, each remembered in `localStorage`: UI **palette**, **table felt** (Green / Blue / Burgundy / Slate / PokerTH / Textured), **card deck** (PokerTH, PokerTH 1.0, PokerTH new, Green Casino), **action buttons** (Flat / Glossy), **chip pucks**, and **seat style**\n- **Seat styles** — six theme-aware seat \"packs\", switchable like decks: **Classic** (the historical render), **Chip**, **Plate**, **Card**, **Compact**, and **Bar**. Pack names stay in English across all languages (like the poker terms). The default adapts to the device — **Compact on phones**, **Plate on tablet \u0026 desktop** — and any explicit choice is saved in `localStorage` and always wins\n- **Light \u0026 dark aware**: every theme carries its own `color-scheme`, and the browser status-bar `theme-color` follows the active theme\n- **Glossy coloured action buttons** (Fold red / Check-Call blue / Raise green / All-In orange) and a live preview of each card deck right in the panel\n- Fully **localized in all 36 languages** and switchable instantly, with no reload\n- Operators can set a **default theme** for first-time visitors (see [the admin panel](#admin-panel))\n\n### Player experience\n- **Emoji avatar** selector: 🎭 button → 500+ icons organised by category (animals, fantasy, fun characters…)\n- Avatars visible by all players in real time (broadcast via proxy `AVATAR:pid:emoji`)\n- **Custom image avatar** — use your own photo instead of an emoji, shared live with the table (broadcast via `AVATARIMG:pid:dataURL`)\n- Anti-flicker cache so avatars survive seat re-renders\n- Bots always show 🤖\n- **Session statistics** panel (click your avatar): hands played, wins, win rate, net gain/loss, best/worst hand, last 5 hands with card history\n- **Family leaderboard** (LAN / private server): a shared per-nickname ranking persisted on the server, **sortable** (net winnings, ¥ per 100 hands, hands played…), with a configurable automatic reset (off / daily / monthly / yearly) plus on-demand reset\n- **Win streak badge** on seats for players on a hot run\n\n### Chat \u0026 reactions\n- In-lobby chat and in-game chat (dropdown panels)\n- **Emoji picker in chat** (desktop) — a 😊 button in the chat input (both lobby and in-game) opens an emoji grid; click to insert it at the cursor, then send like any text. Hidden on mobile, where the native keyboard already provides emojis\n- 30 emoji reactions with a 6-second counter, broadcast to all players\n- **Cross-client reactions**: reactions also travel through a shared `/emoji` chat command (handled like `/me`), so they reach other clients and work on pokerth.net too — while a fast `REACT:` relay stays the web↔web path\n\n### Comfort features\n- Browser notifications when it is your turn (background tab)\n- Tab title flashes: ⚡ YOUR TURN — PokerTH\n- Keyboard shortcuts: **F** = Fold, **C** / Space = Call, **R** = Raise, **A** = All-in, plus **1 / 2 / 3** to arm a ⅓ / ½ / pot bet (then **R** to confirm) — the bet buttons show these keycaps on desktop\n- Sound effects: distinct sounds for fold / check / call / raise / all-in / shuffle / drumroll / bad-beat / win fanfare, plus urgent-timer warning\n- **Full i18n in 36 languages**, switchable on the fly and auto-detected from the browser locale — the complete official PokerTH language set plus community additions (Ukrainian, Romanian, Croatian, Serbian and more), with Brazilian and European Portuguese shipped as separate catalogues (pt-BR / pt-PT)\n- Fullscreen mode on all screens\n- Poker hand reference overlay (? button)\n- Exponential-backoff auto-reconnect with live countdown\n\n### PWA\n- `manifest.json` + Service Worker (`sw.js`) with versioned **network-first** cache\n- New-version notification: the page tells the user when an updated service worker is ready and applies the update on the next reload\n- Installable on mobile and desktop (\"Add to Home Screen\")\n\n---\n\n\u003ca id=\"login-modes-transport\"\u003e\u003c/a\u003e\n## Login modes \u0026 transport\n\nThe client is designed first and foremost for **LAN and private self-hosted servers** — that is its intended use. The connect screen exposes **three server choices** plus a **Guest mode** checkbox (just above the Connect button, **off by default**). One choice — **🏋️ Training mode** — is fully offline (solo play against bots, no server at all); the two online choices, combined with the Guest-mode checkbox, map to the four underlying PokerTH login types, each with its own transport — handy to know when debugging a connection problem.\n\n| Server choice | Guest mode | Login type | Transport | Notes |\n|---|---|---|---|---|\n| **🏋️ Training mode** | — | none — local engine | none — runs in the browser | **100% offline** solo play against bots; no server, proxy, or connection needed (works even as an installed PWA with no internet) |\n| **LAN / Dedicated server** | off *(default)* | Internet guest (`unauth`, type 2) | proxy → TCP or TLS (your choice) | Default for self-hosted setups; in-game chat \u0026 reactions **enabled** |\n| **LAN / Dedicated server** | on | Pure LAN (`lan`, type 0) | proxy → TCP raw | The server **refuses** in-game chat/reactions (reactions stay LAN-local) |\n| **pokerth.net** | on | Guest (`guest`, type 2) | direct TLS WebSocket | Throwaway guest on the public server |\n| **pokerth.net** | off | Registered account (`auth`, type 1) | direct TLS WebSocket | Login + password; TLS auto-enabled |\n\nThe pokerth.net rows connect **directly over a TLS WebSocket, bypassing the proxy**. Please use the public server responsibly and prefer your own LAN or private server for regular play, out of respect for the official PokerTH infrastructure.\n\n---\n\n\u003ca id=\"playing-a-game\"\u003e\u003c/a\u003e\n## Playing a game\n\nJust want to deal a hand with the family? Start a private table in seconds:\n\n\u003ca id=\"quick-start-lan\"\u003e\u003c/a\u003e\n### Quick start — LAN family game\n\n1. Run the proxy on any computer on your local network.\n2. Find that computer's local IP (e.g. `192.168.1.10`).\n3. Open `http://192.168.1.10:8080` on any phone or tablet on the same Wi-Fi.\n4. Leave the server selector on **LAN / Dedicated server** (the default), pick a nickname, and join or create a table.\n5. Deal cards and enjoy!\n\n---\n\n## Architecture\n\nBrowsers cannot open raw TCP/TLS connections to classic PokerTH servers. This project bridges the gap with a tiny Node.js proxy:\n\n```text\nBrowser WebSocket  ⇄  proxy.js (Node.js)  ⇄  PokerTH TCP/TLS server\n```\n\nWhen connecting to the public pokerth.net server, the browser connects directly over a TLS WebSocket and the proxy is bypassed.\n\nBeyond bridging WebSocket frames to the server's raw TCP/TLS stream, `proxy.js` is a small application server. Its functions:\n\n- **Static file server** — serves the client (HTML/JS/CSS and PWA assets) over HTTP, with on-the-fly **brotli/gzip compression** cached by file mtime.\n- **Session persistence \u0026 seamless reconnect** — each browser session is keyed by a `sid`. If the WebSocket drops (e.g. a phone switching Wi-Fi ↔ cellular), the upstream PokerTH connection is **kept alive for a 2-minute grace period**, and the next connection presenting the same `sid` is rebound to it — no re-login, no lost seat. A **heartbeat (ping/pong) plus an RX watchdog** detect genuinely dead sockets.\n- **Clean intentional disconnect** — when the user actively leaves (the ✕ button), the client closes the WebSocket with code **4001**. The proxy treats this as a deliberate quit and tears down the upstream **immediately**, skipping the grace period, so the player/nick is freed on the server right away instead of lingering as a \"ghost\" for ~2 minutes.\n- **Custom broadcast relays** — three application messages are fanned out to the other connected clients. Relays are **scoped per upstream** (`host:port`) so they only reach players on the same server, and oversized frames are dropped:\n\n| Message | Purpose |\n|---|---|\n| `REACT:pid:emoji` | Emoji reaction from a player |\n| `AVATAR:pid:emoji` | Avatar emoji update |\n| `AVATARIMG:pid:dataURL` | Custom image-avatar update |\n\n- **Connection allowlist** — for anti-open-relay safety the proxy only dials servers on a configured allowlist (see the deployment section below).\n- **Helper endpoints** — `GET /__ver` reports the newest mtime of the static assets (this drives the client's \"new version\" banner), `GET /app-config` exposes the operator's client settings (enabled login modes, default theme \u0026 in-game settings, server identity, welcome message), and `GET /stats` serves the shared lifetime leaderboard.\n\n### Repository layout\n\n```text\npokerth-web-client/\n├── proxy.js                 # WS→TCP/TLS proxy + static HTTP server\n├── public/\n│   ├── pokerth-client.html  # HTML shell + inline head scripts\n│   ├── admin.html           # Maintainer console (served at /admin)\n│   ├── pokerth.js           # Full application logic\n│   ├── pokerth.css          # Styles\n│   ├── manifest.json        # PWA manifest\n│   ├── sw.js                # Service Worker (versioned cache)\n│   ├── modules/             # ES modules\n│   │   ├── i18n.mjs         #   internationalisation (36 languages)\n│   │   ├── sounds.mjs       #   sound effects\n│   │   ├── theme.mjs        #   theming engine (palettes, decks, seats, presets)\n│   │   ├── lang/            #   36 language catalogues\n│   │   └── offline/         #   local game engine + bots (Training mode)\n│   ├── proto/               # Protobuf bundle \u0026 helpers\n│   └── favicon-*.png        # PWA icons\n├── docs/\n│   ├── PROJECT.md\n│   ├── ROADMAP.md\n│   ├── SECURITY.md\n│   └── screenshots/         # Screenshots used in this README\n├── scripts/\n│   ├── build-proto.mjs      # Regenerates the protobuf bundle from .proto\n│   └── reset-stats.mjs      # Clears the family leaderboard (npm run stats:reset)\n├── install.sh               # Installer / updater / uninstaller (one-liner)\n├── Dockerfile               # Multi-arch image (node:20-alpine base)\n├── docker-compose.yml       # One-shot self-host config\n├── package.json\n├── LICENSE                  # AGPL-3.0-or-later\n└── README.md\n```\n\n---\n\n## Requirements\n\n- **Node.js 18** or newer (Node 20 LTS recommended)\n- **npm** (shipped with Node.js)\n- **git**\n- A modern browser (Chrome, Firefox, Safari, Edge)\n- A running PokerTH server (local LAN, your own remote server, or pokerth.net)\n\n---\n\n\u003ca id=\"self-hosting\"\u003e\u003c/a\u003e\n## Self-hosting\n\nRun your own PokerTH proxy with whichever method suits you:\n\n- [Quick install (one-liner)](#quick-install-one-liner) — fastest, on Debian/Ubuntu\n- [Docker](#docker) — containerised, multi-arch\n- [Manual installation](#manual-installation) — step by step, full control\n- [Self-hosting on a Raspberry Pi](#raspberry-pi) — host it at home\n\nOnce running, manage it from one place — see [Managing \u0026 resetting](#managing-resetting).\n\n---\n\n## Quick install (one-liner)\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003e📂 Show the one-liner install guide\u003c/b\u003e\u003c/summary\u003e\n\nOn a fresh **Debian/Ubuntu** machine you can install everything — Node.js, PM2, the project, and a boot-persistent service — with a single command:\n\n```bash\ncurl -sSL https://raw.githubusercontent.com/narmod/pokerth-web-client/HEAD/install.sh | bash\n```\n\nThe installer asks a couple of questions (port, LAN/TLS mode, install directory), then runs the proxy under PM2 as a **non-root** user with start-on-boot. It is safe to re-run: an existing install is updated rather than duplicated.\n\n\u003e **Prefer to read before you run?** A healthy instinct for any `curl | bash` installer. Download and inspect it first:\n\u003e\n\u003e ```bash\n\u003e curl -sSL https://raw.githubusercontent.com/narmod/pokerth-web-client/HEAD/install.sh -o install.sh\n\u003e less install.sh        # review what it does\n\u003e bash install.sh        # then run it\n\u003e ```\n\nWhen run without a terminal (CI / automation) the installer is fully non-interactive and takes its settings from environment variables:\n\n| Variable | Default | Purpose |\n|---|---|---|\n| `PORT` | `8080` | HTTP / WebSocket port |\n| `NO_TLS` | _(unset)_ | set to `1` for LAN mode (`--notls`) |\n| `INSTALL_DIR` | `\u003crun-user home\u003e/pokerth-web-client` | install location |\n| `RUN_USER` | invoking user, or `pokerth` when run as root | non-root user that runs PM2 |\n| `APP_NAME` | `pokerth-web` | PM2 process name |\n| `SETUP_FIREWALL` | _(unset)_ | set to `1` to open the port in `ufw` |\n| `ASSUME_YES` | _(unset)_ | set to `1` to skip the confirmation prompt |\n| `STATS_RESET_PERIOD` | `monthly` | leaderboard auto-reset: `off` / `daily` / `monthly` / `yearly` |\n| `STATS_ADMIN_TOKEN` | _(unset)_ | token enabling the remote leaderboard-reset endpoint |\n\nExample:\n\n```bash\nPORT=8090 NO_TLS=1 ASSUME_YES=1 \\\n  bash -c \"$(curl -sSL https://raw.githubusercontent.com/narmod/pokerth-web-client/HEAD/install.sh)\"\n```\n\nFor HTTPS (recommended — many mobile browsers block plain `ws://`), follow the Nginx + Let's Encrypt steps in the manual installation below.\n\n\u003c/details\u003e\n\n---\n\n\u003ca id=\"docker\"\u003e\u003c/a\u003e\n## Docker\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003e📂 Show the Docker guide\u003c/b\u003e\u003c/summary\u003e\n\nThe repository ships with a `Dockerfile` and a `docker-compose.yml`. By default Compose **pulls a prebuilt multi-architecture image** from GHCR (`amd64` / `arm64` / `armv7`), so there is **nothing to compile** — ideal on a Raspberry Pi.\n\n**1. Configure the allowlist.** For anti-open-relay reasons the proxy only dials servers on an allowlist. `pokerth.net` works out of the box, but to reach **your own** LAN / private server you must add it. Copy the example env file and edit it:\n\n```bash\ncp .env.example .env\n# then edit .env and add your server to ALLOWED_HOSTS\n```\n\n```dotenv\n# .env\nPORT=8080\nALLOWED_HOSTS=pokerth.net,www.pokerth.net,mybox.ddns.net,192.168.1.10\n```\n\n\u003e If your PokerTH server runs on the **same machine** as Docker, use `host.docker.internal` (Docker Desktop) or the host's LAN IP in both `ALLOWED_HOSTS` and the connect form — **not** `localhost`, which from inside the container points to the container itself.\n\n**2. Start it:**\n\n```bash\ndocker compose up -d      # pulls the prebuilt image and starts the proxy\ndocker compose pull       # later: fetch the newest image, then `up -d` again\n```\n\nThe proxy will be available on `http://\u003chost\u003e:8080/` (or whatever `PORT` you set).\n\n**Without Compose** (e.g. a quick run on a Pi):\n\n```bash\ndocker run -d --name pokerth-web -p 8080:8080 \\\n  -e ALLOWED_HOSTS=pokerth.net,www.pokerth.net,mybox.ddns.net \\\n  -v pokerth-stats:/data \\\n  ghcr.io/narmod/pokerth-web-client:latest\n```\n\nNotes:\n- The container runs as the non-root `node` user.\n- The shared **family leaderboard** (`stats.json`) is persisted in a named volume (`pokerth-stats`), so it survives `docker compose down \u0026\u0026 up`. (Per-device session stats live in each browser, not on the server.)\n- Set `STATS_RESET_PERIOD` (and optionally `STATS_ADMIN_TOKEN`) in `.env` to control the leaderboard auto-reset — see [Resetting the family leaderboard](#leaderboard-reset).\n- A healthcheck pings the HTTP server every 30 s; `docker ps` shows the container as `healthy` once it is up.\n- `PORT` only changes the **published host port** — the container always listens on `8080` internally.\n- Prefer to build the image yourself? Comment out `image:` in `docker-compose.yml` and uncomment `build: .`.\n\n\u003c/details\u003e\n\n---\n\n\u003ca id=\"manual-installation\"\u003e\u003c/a\u003e\n## Manual installation (Ubuntu / Debian)\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003e📂 Show the full step-by-step guide\u003c/b\u003e\u003c/summary\u003e\n\nPrefer to do it by hand, or need a custom setup? These are the full steps the one-liner automates. This walkthrough assumes a clean Ubuntu 22.04 / 24.04 or Debian 12 VPS. Adapt commands for other distributions.\n\n### 1. Update the system\n\n```bash\nsudo apt update \u0026\u0026 sudo apt upgrade -y\n```\n\n### 2. Install build tools, git and curl\n\n```bash\nsudo apt install -y curl git build-essential\n```\n\n### 3. Install Node.js 20 LTS (via NodeSource)\n\nThe Node.js shipped in Ubuntu's default repos is often too old. Use the official NodeSource repo to get a recent LTS:\n\n```bash\ncurl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -\nsudo apt install -y nodejs\n```\n\nVerify:\n\n```bash\nnode -v   # should print v20.x.x or newer\nnpm -v    # should print 10.x or newer\n```\n\n### 4. Install PM2 globally (process manager)\n\nPM2 keeps the proxy alive in the background and restarts it automatically at boot or after a crash.\n\n```bash\nsudo npm install -g pm2\n```\n\n### 5. Clone the project and install dependencies\n\n```bash\ngit clone https://github.com/narmod/pokerth-web-client.git\ncd pokerth-web-client\nnpm install\n```\n\nYou may see two `npm WARN deprecated` lines about `inflight` and `glob` — these are pulled in by `protobufjs-cli` (a dev-only dependency used to rebuild the protobuf bundle). They are harmless at runtime. `npm audit` should report **0 vulnerabilities**.\n\n### 6. Open the firewall\n\nIf `ufw` is active on the server:\n\n```bash\nsudo ufw allow 8080/tcp     # if you serve directly on 8080\n# OR (recommended) 80/443 if you put Nginx in front\nsudo ufw allow 80/tcp\nsudo ufw allow 443/tcp\n```\n\nCloud providers (IONOS, OVH, Hetzner, etc.) often have their **own firewall in front of the VPS** that is independent of `ufw`. Make sure to open the same ports in their control panel too, otherwise the port stays unreachable from outside.\n\n### 7. Start the proxy with PM2\n\n```bash\npm2 start proxy.js --name pokerth-web\npm2 save\npm2 startup       # then run the command it prints, to enable boot-time start\n```\n\nVerify:\n\n```bash\npm2 status\npm2 logs pokerth-web --lines 30\n```\n\nThe client is now live at `http://\u003cyour-server-ip\u003e:8080`.\n\n### 8. (Recommended) Add HTTPS with Nginx + Let's Encrypt\n\nA direct WebSocket on port 8080 works, but many mobile browsers and corporate networks **block plain `ws://` connections**. Adding HTTPS via Nginx solves this for free and gives you a clean URL.\n\nYou need a domain name pointing to the server's IP. Free options include [No-IP](https://www.noip.com/) (`yourname.ddns.net`) or [DuckDNS](https://www.duckdns.org/) (`yourname.duckdns.org`).\n\nInstall Nginx and Certbot:\n\n```bash\nsudo apt install -y nginx certbot python3-certbot-nginx\n```\n\nCreate `/etc/nginx/sites-available/pokerth` (replace `your-domain.example` with your real hostname):\n\n```nginx\nserver {\n    listen 80;\n    server_name your-domain.example;\n\n    location / {\n        proxy_pass http://localhost:8080;\n        proxy_http_version 1.1;\n        proxy_set_header Upgrade $http_upgrade;\n        proxy_set_header Connection \"upgrade\";\n        proxy_set_header Host $host;\n        proxy_set_header X-Real-IP $remote_addr;\n        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n        proxy_set_header X-Forwarded-Proto $scheme;\n        proxy_read_timeout 86400;\n        proxy_send_timeout 86400;\n    }\n}\n```\n\nEnable and reload:\n\n```bash\nsudo ln -s /etc/nginx/sites-available/pokerth /etc/nginx/sites-enabled/\nsudo rm -f /etc/nginx/sites-enabled/default\nsudo nginx -t\nsudo systemctl reload nginx\n```\n\nObtain the certificate (Certbot will edit the config to add HTTPS automatically):\n\n```bash\nsudo certbot --nginx -d your-domain.example\n```\n\nPick option `2 — Redirect HTTP to HTTPS` when asked. Renewal is automatic via a systemd timer.\n\nThe client is now live at `https://your-domain.example`. In the connect form, the WebSocket Proxy URL field will auto-fill with `wss://your-domain.example` (the JS detects the protocol).\n\n### 9. Updating later\n\nUpdate in one command — see [Managing \u0026 resetting](#managing-resetting):\n\n```bash\nsudo pokerth-web update\n```\n\nIf you set things up manually (no `pokerth-web` command), update from your checkout: `git pull --ff-only`, then `npm install --omit=dev` and `pm2 restart pokerth-web --update-env \u0026\u0026 pm2 save`.\n\n\u003c/details\u003e\n\n---\n\n\u003ca id=\"raspberry-pi\"\u003e\u003c/a\u003e\n## Self-hosting on a Raspberry Pi 🥧\n\nBoth the PokerTH server **and** this web proxy are extremely light: PokerTH is a turn-based card game exchanging small Protobuf messages, so a 10-player table is a trivial load. The players' phones do all the rendering — the Pi just relays messages and serves static files. That makes a Pi a perfect always-on box for family / LAN games.\n\n**Which model?**\n\n| Model | Verdict |\n|---|---|\n| **Pi 4 (2 GB)** | ✅ Recommended sweet spot — Gigabit Ethernet, comfortable headroom, smooth `npm install` / `git`. |\n| **Pi 5** | Overkill but fastest; great if you want room for other services. |\n| **Pi 3B+ (1 GB)** | Works fine for runtime. |\n| **Pi Zero 2 W (512 MB, Wi-Fi only)** | Not recommended — tight RAM for `npm install`, no wired Ethernet. |\n\n**For a smooth game, the network matters more than the Pi:**\n\n- Connect the Pi to your router by **wired Ethernet** — the server stays rock-solid.\n- The quality of your **Wi-Fi / access point** affects the 10 players more than the Pi's CPU.\n- Prefer booting from a **USB SSD** (Pi 4/5) over a microSD for reliability with PM2 logs; otherwise use a good A1/A2 card.\n\n**Install:**\n\n1. Flash a **64-bit, Debian-based OS** — **Raspberry Pi OS (64-bit) is recommended** (Debian arm64 works too). An `apt`-based system is required: the one-liner installer needs `apt` and stops cleanly on non-apt distros (Alpine, Fedora…).\n2. Install the PokerTH server — it is packaged for Debian / Ubuntu including ARM:\n   ```bash\n   sudo apt update \u0026\u0026 sudo apt install pokerth-server\n   ```\n   (If your distro doesn't ship it, build it from the [upstream sources](https://github.com/pokerth/pokerth).) Run `pokerth_server`; it listens on TCP **7234** by default.\n3. Install the web proxy with the one-liner — it sets up Node.js 20, PM2, the project and a boot service, and works on ARM:\n   ```bash\n   curl -sSL https://raw.githubusercontent.com/narmod/pokerth-web-client/HEAD/install.sh | bash\n   ```\n   Prefer to do it by hand, or inspect the script first? See [Manual installation](#manual-installation).\n4. From any phone on the same Wi-Fi, open `http://\u003cpi-ip\u003e:8080`, leave the server on **LAN / Dedicated server**, and deal.\n\n\u003e **PWA extras (install to home screen, offline, notifications) need HTTPS** — see [Known limitations](#known-limitations). The game itself works perfectly over plain `http://` / `ws://` on the LAN.\n\n---\n\n\u003ca id=\"install-the-app\"\u003e\u003c/a\u003e\n## Install the app (as a PWA)\n\nOnce your proxy is running, open the client and install it **from your own server's address** (the URL the proxy serves — e.g. `https://your-domain` or `http://192.168.x.x:8080`). The connect form auto-fills the WebSocket Proxy URL and the PokerTH host from the page it is served on, and a PWA is cached under that origin — so the installed app keeps **your** server pre-loaded and you never retype it. Installing from any other address would point it elsewhere and force you to re-enter the server every time.\n\n\u003e A fully installable PWA (standalone window, offline cache, notifications) needs a **secure context** — HTTPS, or `localhost`. Set up [Nginx + HTTPS](#manual-installation) for the install option to appear; over plain LAN `http://` the game still works in the browser, but the install prompt won't show.\n\nThen pin it like a native app:\n\n- **Desktop — Chrome / Edge / Brave (Windows, macOS, Linux, ChromeOS):** click the **install icon** in the address bar (or the ⋮ menu → **Save and share → Install page as app**) → **Install**. It opens in its own window, without tabs or toolbar. ⚠️ If you use **Create shortcut** instead, tick **\"Open as window\"** — otherwise it just opens as an ordinary browser tab, not a standalone full-window app.\n- **Android (Chrome / Edge / Brave):** browser menu → **Install app** / **Add to Home screen**.\n- **iPhone / iPad:** in **Safari**, tap **Share → Add to Home Screen → Add**. (iOS installs PWAs from Safari only; since iOS 17, Chrome and Edge also offer it via their Share button.)\n\nThe in-app **📲 Install** hint appears automatically when your browser supports installation. Once installed, **Training mode** runs with no internet at all; online play (LAN, your dedicated server, or pokerth.net) just needs to reach the chosen server.\n\n---\n\n\u003ca id=\"managing-resetting\"\u003e\u003c/a\u003e\n## Managing \u0026 resetting\n\n\u003ca id=\"managing-the-service\"\u003e\u003c/a\u003e\n### Managing the service\n\nAfter a first install, a `pokerth-web` command is available to manage everything — no need to touch PM2 by hand:\n\n```bash\nsudo pokerth-web update              # pull the latest version, reinstall deps, restart\npokerth-web status                   # show the PM2 status\nsudo pokerth-web set-period yearly   # leaderboard auto-reset: off | daily | monthly | yearly\nsudo pokerth-web reset-stats         # wipe the family leaderboard now\nsudo pokerth-web set-token SECRET    # admin token: unlocks the admin panel + remote reset (no token = both off)\nsudo pokerth-web admin off           # hide the admin panel (/admin returns 404); 'on' to show it again\nsudo pokerth-web uninstall           # stop and remove the service\npokerth-web help                     # list every command\n```\n\n`set-period`, `set-token` and `admin on|off` are saved to `/etc/pokerth-web.conf` and **re-applied automatically on every update and reboot**, so you set them once.\n\nThe same actions work through the one-liner if you prefer not to use the command:\n\n```bash\ncurl -sSL https://raw.githubusercontent.com/narmod/pokerth-web-client/HEAD/install.sh | bash -s -- update\n```\n\n`uninstall` removes the PM2 service, its boot entry, the state file and the `pokerth-web` command, then asks separately before deleting the install directory or the dedicated service user. It never touches Node.js, PM2 or apt packages.\n\n\u003ca id=\"admin-panel\"\u003e\u003c/a\u003e\n### The admin panel\n\nA self-hosted maintainer console lives at **`/admin`** (e.g. `https://your-host/admin`). It is governed by two independent switches:\n\n- **Visibility — `pokerth-web admin on|off`.** When **off**, `/admin` and every `/admin/*` route return a plain `404`, so the panel is fully hidden — not merely inert. **On** is the default and serves the panel. The setting is saved to `/etc/pokerth-web.conf` and re-applied on every update and reboot.\n- **Authentication — `pokerth-web set-token \u003ctoken\u003e`.** Every action in the panel requires this token; with no token set, the page still loads but all actions are refused. The same token also guards the remote `/stats` reset endpoint.\n\nA good rule of thumb: set a token before relying on the panel, and run `pokerth-web admin off` whenever you don't need it exposed. Always serve it over **HTTPS** — the panel sends the token in an `Authorization: Bearer` header, so it never lands in URLs, logs or browser history.\n\nTo use it, open `/admin`, paste your token and **Log in**. The console is organised into five tabs:\n\n- **Server** — live status (version, uptime, connected players, open sockets); one-click self-update **with or without a restart**; schedule a restart or update with a countdown banner shown to players; tune **proxy settings** (extra allowed hosts, session-grace window, connection gap); and view, clear or act on the logs.\n- **Clients** — what new visitors get by default: which **login modes** (Offline / LAN / pokerth.net) appear on the connect screen, the **default login form** (mode + host), a **default theme**, **default in-game settings** (BB display, assistance, quick-bet, auto-action, voice, vibration), **default table-creation settings** (max players, small blind, starting stack, action timer), and a **server identity** (name + tagline) that replaces \"PokerTH\" / \"Web Client\" on the login screen.\n- **Broadcasts** — send a message to all connected players right now, or on a **recurring schedule** (interval / daily / every-N-days / weekly / monthly / once, with an icon, end date and max sends); plus an editor for the multilingual **welcome / rules message** shown on a player's first visit — with **on-device auto-translation** (where the browser supports it) to fill languages you haven't written yourself.\n- **Leaderboard** — reset the shared leaderboard immediately and set its auto-reset period.\n- **Packages** — install or remove gallery **card decks** and **table styles** from a `.zip` file or URL, and **enable/disable** each one without deleting its files.\n\n\u003ca id=\"leaderboard-reset\"\u003e\u003c/a\u003e\n### Resetting the family leaderboard\n\nThe shared leaderboard (per-nickname **lifetime** stats) lives in `stats.json` on the server. Per-device **session** stats stay in each browser and are never touched by any of this.\n\n**Automatic reset.** The `STATS_RESET_PERIOD` environment variable controls how often the leaderboard wipes itself — `off`, `daily`, `monthly` (**default**) or `yearly`. The boundary is the server's local time, and the current period is remembered, so a restart never triggers a false reset.\n\nWith the one-liner installer, set it with a single command — it's saved and re-applied automatically on every update and reboot:\n\n```bash\nsudo pokerth-web set-period yearly     # off | daily | monthly | yearly\n```\n\nYou can also choose it at install time: `curl -sSL .../install.sh | STATS_RESET_PERIOD=yearly bash`.\n\n**On-demand reset — command line.** If you used the one-liner installer, a single command wipes the leaderboard and restarts the service:\n\n```bash\nsudo pokerth-web reset-stats\n```\n\nFrom a manual checkout, do it by hand:\n\n```bash\nnpm run stats:reset          # empties stats.json\npm2 restart pokerth-web      # restart so a running proxy drops its in-memory copy\n```\n\n**On-demand reset — remote.** Set an `STATS_ADMIN_TOKEN`, then POST to `/stats` (the endpoint stays disabled until a token is set):\n\n```bash\ncurl -X POST http://your-host:8080/stats \\\n     -H 'Content-Type: application/json' \\\n     -d '{\"_resetAll\":true,\"token\":\"YOUR_TOKEN\"}'\n```\n\nWith the one-liner installer, set or clear that token in one command (saved and re-applied like the period): `sudo pokerth-web set-token YOUR_TOKEN` (run it with no token to disable the endpoint).\n\nSet these via `.env` under Docker, or pass them when (re)starting PM2, e.g. `STATS_RESET_PERIOD=yearly pm2 restart pokerth-web --update-env`.\n\n---\n\n\u003ca id=\"development\"\u003e\u003c/a\u003e\n## Development (running from source)\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003e📂 Show the local-development guide\u003c/b\u003e\u003c/summary\u003e\n\nIf you only want to play around on your own machine:\n\n```bash\ngit clone https://github.com/narmod/pokerth-web-client.git\ncd pokerth-web-client\nnpm install\n```\n\n### Standard (TLS enabled, recommended)\n\n```bash\nnpm start\n```\n\nThen open **http://localhost:8080** in your browser.\n\n### LAN (no TLS)\n\n```bash\nnpm run start:lan\n```\n\n### Custom port\n\n```bash\nnode proxy.js 8090\n```\n\n### Development (ignore untrusted TLS certificate)\n\n```bash\nnpm run start:insecure\n```\n\n\u003e ⚠️ `--insecure` disables TLS certificate verification. Use only for local development.\n\n\u003c/details\u003e\n\n---\n\n## Protocol notes\n\nPokerTH speaks a length-prefixed Protobuf-based protocol over TCP. This client parses and emits a hand-written subset of those messages — there is no full Protobuf runtime in the browser, which keeps the bundle small.\n\nA few things worth knowing if you plan to hack on this:\n\n- The proxy logs every parsed message in hex with a short description, which makes protocol debugging straightforward (`pm2 logs pokerth-web` if you run under PM2).\n- Wire-type field numbers used by this client are documented inline in `public/pokerth.js` next to each `Proto.encode([...])` call, with references to `pokerth.proto` in the upstream repository.\n\n---\n\n## Known limitations\n\n- The Protobuf protocol is still handled by a small hand-written encoder/decoder rather than generated classes.\n- The bulk of the logic still lives in a single `pokerth.js` file, though i18n, sounds and the protocol layer have already been extracted into ES modules. Further splitting would help.\n- More automated protocol tests are needed before calling the client production-ready.\n- Spectator mode works but lacks a few quality-of-life touches (e.g. you cannot see other players' cards at showdown the same way the native client does).\n- **Training-mode bots use a simple heuristic AI.** They're perfect for learning the flow, practising the interface, or playing offline with no server — but they won't bluff or adapt like a strong human opponent.\n- **PWA features (install to home screen, offline Service Worker, background notifications) require a *secure context*** — i.e. HTTPS, or `localhost`. Over plain `http://` on a LAN IP (e.g. `192.168.1.10:8080`) the game plays perfectly, but the browser disables those three features by design. To get them on a LAN, serve the client over HTTPS — e.g. [`mkcert`](https://github.com/FiloSottile/mkcert) for a locally-trusted certificate, a self-signed cert, a real domain with Let's Encrypt, or a tunnel such as Cloudflare Tunnel / Tailscale.\n- **Translations are not yet natively reviewed.** The 36 language catalogues were produced with care but are largely machine-assisted, so some wordings — especially poker-specific terms — may be imperfect, the less common languages (e.g. Scottish Gaelic, Tamil) most of all. Corrections via issue or pull request are very welcome.\n\n---\n\n\u003ca id=\"roadmap\"\u003e\u003c/a\u003e\n## Roadmap / Suggested next steps\n\n1. **Adopt the generated Protobuf bindings.** A protobuf.js runtime and classes generated from `pokerth.proto` now live in `public/proto/`; what remains is switching `pokerth.js` over from its inline hand-written codec to that bundle.\n2. Split the client into maintainable ES modules *(in progress — i18n, sounds and the Protobuf bindings are already extracted; the bulk still lives in `pokerth.js`)*.\n3. Add automated protocol tests with a mock PokerTH server.\n4. Polish reconnection edge cases *(currently exponential backoff, capped at 3–6 attempts depending on the transport)*.\n5. Custom-emoji / image avatar import *(the built-in picker already ships 500+ emojis)*.\n6. A read-only embed for streamers *(spectating a table already works; this would add a dedicated streamer-friendly view)*.\n7. Native review of the machine-assisted translations *(36 languages ship today; some wordings — poker terms especially — would benefit from a native pass)*.\n\n---\n\n## License\n\nThis project is licensed under the **GNU Affero General Public License v3.0 or later** — the same license as PokerTH itself.\n\n---\n\n## Acknowledgements\n\nA huge thank you to the entire **PokerTH team** for creating and maintaining such a wonderful open-source poker game over all these years. This project would not exist without your work. 🙏\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnarmod%2Fpokerth-web-client","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnarmod%2Fpokerth-web-client","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnarmod%2Fpokerth-web-client/lists"}