🍏 iOS / Mac OSAlles neu in iOS 27.2 Beta 1(17.09.2026 um 09:36 Uhr)
🍏 iOS / Mac OSiOS 27.2 Beta Adds Dual Capture to Group FaceTime Calls(17.09.2026 um 08:05 Uhr)
🍏 iOS / Mac OSAlles neu in iOS 27.2 Beta 1(17.09.2026 um 09:36 Uhr)
🍏 iOS / Mac OSiOS 27.2 Beta Adds Dual Capture to Group FaceTime Calls(17.09.2026 um 08:05 Uhr)
🔧 Programmierung 🕛 vor 2 Monaten 5 Min Lesezeit
0

I Built a Snooker Scoring PWA Because the Game Room Scoreboard Only Had 2 Slots

↗ Quelle (dev.to)
🗣️ Stimme:

I Built a Snooker Scoring PWA Because the Game Room Scoreboard Only Had 2 Slots



We were three friends. One scoreboard. Zero solutions.



Every time my friends and I hit the snooker table, we'd run into the same problem — the scoreboard on the wall was built for two players only. One of us always had to manually keep score in their head, or on a piece of paper, or just... trust the other two. Not ideal when points are on the line.



So I did what any developer would do. I built an app.



That app is SnookerBee — a mobile-first Progressive Web App for tracking snooker scores, built with React 19, TypeScript, Vite, Supabase, and Google OAuth. It's live at snookerbee.vercel.app and the full source is on GitHub.



Here's how I built it, what I learned, and why a PWA was the right choice.



The Problem



Snooker is typically played 1v1. Most scoreboards, apps, and trackers assume this. But snooker rooms often have 3-4 people sharing a table in a free-for-all — rotating turns, individual scores, tracking who won which frame.



I couldn't find an app that:



Supported more than 2 players properly

Worked offline (game rooms often have spotty Wi-Fi)

Felt designed for snooker specifically — not a generic "sports tracker"

Was installable on mobile without going through an App Store



So I decided to build exactly that.



Tech Stack



LayerTechnologyFrontendReact 19 + TypeScriptBuild ToolViteRoutingReact Router v7StylingVanilla CSS (custom tokens, glassmorphism)PWAvite-plugin-pwa + WorkboxAudioWeb Audio APIBackendSupabase (PostgreSQL)AuthGoogle OAuth via Supabase AuthHostingVercel



Features



🎮 Three Game Modes



1v1 — Standard head-to-head

Teams — 2v2 or 3v3 alternating configurations

Free-for-All — 2 to 8 players, round-robin turns



This was the core reason I built it — Free-for-All mode handles exactly the situation my friends and I were in.



⚖️ Strict Rules Engine



The scoring logic is built as a pure state-machine reducer. It handles:



Official red-color sequence enforcement

Free ball nomination

Re-spotted black on tied scores

Turn rotation management across all game modes

10-step deep-cloned undo stack



Getting this right was honestly the hardest part of the project. Snooker rules have a lot of edge cases — especially around fouls, free balls, and end-game scenarios.



🔊 Sound Effects — Zero Dependencies



Instead of importing a sound library, I used the Web Audio API directly — oscillators, gains, and envelopes — to synthesize:



Pot confirmation chimes

Foul warning buzzes

Break milestone sounds (25, 50, 100 points)

Victory fanfare



This keeps the bundle lean and works completely offline.



📶 Offline-First PWA



Using vite-plugin-pwa with Workbox caching strategies, the app:



Installs directly to your phone homescreen

Works without internet after first load

Has a custom app icon



This was critical for the game room use case — you don't want your scoring app to fail mid-match because of Wi-Fi.



☁️ Cloud Sync with Supabase



When you're signed in with Google, SnookerBee syncs:



Full match history

Player statistics (highest break, frames won, fouls)

Frame-level action logs



The database uses Row Level Security (RLS) so each user can only access their own data.



Database Schema



Three tables power the cloud sync:



sql-- Matches table

create table matches (

id uuid primary key default gen_random_uuid(),

user_id uuid references auth.users(id) on delete cascade,

mode text not null,

reds_count int not null,

best_of int not null,

created_at timestamptz default now(),

duration_ms bigint not null,

winner_name text not null

);



-- Players per match

create table match_players (

id uuid primary key default gen_random_uuid(),

match_id uuid references matches(id) on delete cascade,

player_name text not null,

team_name text,

total_score int default 0,

highest_break int default 0,

frames_won int default 0,

fouls_committed int default 0,

time_spent_ms bigint default 0

);



-- Frame-level logs

create table match_frames (

id uuid primary key default gen_random_uuid(),

match_id uuid references matches(id) on delete cascade,

frame_number int not null,

duration_ms bigint,

action_log jsonb

);



All three tables have RLS enabled with user-scoped policies.



Lessons Learned




  1. PWA is underrated for sports apps

    The offline capability + homescreen install covers 90% of what a native app gives you, without the App Store overhead. For a game room tool, this is ideal.


  2. Web Audio API is powerful and underused

    I expected to need a library for sounds. Turns out the native API handles everything I needed with less than 100 lines of code, and zero extra bundle weight.


  3. State machines for game logic are worth the upfront cost

    I almost built the scoring logic with scattered useState calls. Switching to a reducer-based state machine early saved me from dozens of bugs later — especially with undo functionality.


  4. Supabase RLS is your friend

    Row Level Security meant I never had to write server-side authorization code. The database enforces it at the query level.




What's Next



Android app via TWA (Trusted Web Activity) / Bubblewrap — Play Store submission

iOS app via Capacitor.js wrapper

Tournament mode — bracket-style multi-match tournaments

Bluetooth scoreboard integration (experimenting)



Try It



🌐 Live App: snookerbee.vercel.app

💻 GitHub: github.com/arshad0126/snookerbee



If you play snooker with more than one friend — give it a try. And if you find a bug or want to contribute, PRs are welcome.



Built with React 19, TypeScript, Vite, Supabase, and a lot of missed pots.



Tags: #webdev #react #pwa #supabase #typescript #buildinpublic #javascript #opensource

Vollständiger Original-Artikel
Den kompletten Beitrag mit allen Details direkt auf dev.to lesen.
↗ Original-Artikel auf dev.to lesen
Wie bewertest du diesen Beitrag?
1 Klick Feedback
Teilen mit Netzwerk & Team:

Community-Analysen & Experten-Meinungen 0

Verfasse deine eigene Analyse, teile Workarounds oder diskutiere diesen Vorfall im Blog.
Noch keine Community-Analyse verfasst. Markiere einen Textabschnitt oder klicke oben auf Eigene Analyse verfassen“!
Community Pulse: Relevanz-Einschätzung
1 Klick Experten-Votum
🔴 Akute Relevanz 0%
🟡 In Evaluierung 0%
🟢 Keine Auswirkung 0%
Spannende Innovation 0%
Verwandte Story-Cluster & Quellen (Vektor-KI)
Port 8095 Engine
1 Quelle
Windows 11 startet nicht: So findet ihr die Ursache und behebt sie
1 Quelle
Belegen Sie die Copilot-Taste neu und starten Sie damit Ihre Lieblings-App
1 Quelle
WinZip
Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten I Built a Snooker Scoring PWA Because the Game Room Scoreboard Only Had 2 Slots

Thematisch verwandte Begriffe: Built, Snooker, Scoring, Because · 6 Treffer

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...