Case study · Diamond Tabletop
Real-time play across phones that survives a dropped connection
Diamond Tabletop is eight card and board games for Android and iOS, with bots for empty seats and live multiplayer between any mix of phones. Behind it are one phone acting as the authority, a server I built and run, and game rules that update without an app release.
- 8 gamesHearts, Rummy, Gin Rummy, Oh Heck and four more, none of them packaged in the app
- 93%of the app's code written once, in shared Kotlin
- 30 sbefore a bot covers a silent player's seat, which stays theirs to reclaim
- Kotlinend to end: both apps, the server, and the scripts the games are written in
The brief
- Friends play together on any mix of iPhones and Androids, on Wi-Fi or cellular.
- A phone that goes to the background, loses signal or gets killed must not break the game for everyone else.
- New games and rules fixes reach players without waiting for an app release or App Review.
What I built
- One device as the source of truth. One phone runs the rules and broadcasts the state; the others send input and draw what changed. The host is elected by measuring each phone's connection, and the same rule re-elects mid-game.
- Recovery as a designed feature, not an afterthought. When the host's connection comes back, it re-sends the whole table. A player whose app was killed can reopen it, enter the room code and be told which seat is theirs. Reconnecting through a server restart is proven in CI against the real server. A full game has been played across networks, a desktop on a LAN against a phone on cellular, through backgrounding and airplane-mode reconnects.
- The server. A Ktor relay and signalling service over WebSockets, with public and private rooms,
and optional accounts on Postgres. Deployed and running at
relay.diamondtabletop.com. - Content as data. Each game's rules are a script, and its board layout and house rules are data in the same bundle. The app downloads its whole library from a published catalog, checking every file against its SHA-256 hash, so a new game or a rules fix needs no release.
- Release. Both stores, with an App Store build anyone can reproduce from the repository and store screenshots produced by the build.
A release decision
The desktop app builds, runs, hosts games and has a working installer. I still didn't ship it. A desktop app with no store has no auto-update, so every install would become a permanently out-of-date client talking to a server and a catalog that keep moving. Android and iOS launched; desktop stayed a development and test target.
Every platform you ship is a platform you support for years. Part of my job is telling you which ones aren't worth it yet, and why.
What this could look like for you
-
A companion app for your device
The hard problems are the same shape: a connection that drops, one device that holds the truth, and state that has to come back without the user noticing. Here the authority is a host phone; for you it's your hardware over BLE. I've built that version too: Weber's smart-grill companion app, rated 4.6★ by 134,000 users, streamed live temperature and cook state over BLE and Wi-Fi through multi-hour cooks.
-
Changes that don't wait for App Review
Device profiles, configuration, content and rules shipped as verified data from a catalog you control, so a fix on Tuesday reaches users on Tuesday.
-
A backend in the same language as the app
If your app needs a relay, accounts or a sync service, I build and deploy it in Kotlin, sharing the same message types as the app, so the two can't quietly drift apart.
-
Multi-user, real-time features
Shared sessions, live views of the same data, several people's phones connected to one device: the same host, room and reconnect design carries over.
Its sibling app
Napkin Scores, a score pad for card nights, is built on the same shared modules and brings players to Diamond Tabletop. Read its case study.
Have a connection that has to hold up?
Tell me the date you're working against. Thirty minutes, technical, no deck.
