Build Logs
Building Wordy Royale: Three Weeks, and the Four Things That Broke
We built the coin-economy core of a multiplayer word game platform in about three weeks: what shipped, four things that broke along the way, and the tooling that keeps a play-money ledger honest.
On this page
We started Wordy Royale about three weeks ago with a simple pitch: a multiplayer word game that feels like walking into an arcade — you look at a wall of tables, pick one, and you’re playing inside two taps. No lobby codes, no “create a room” flow, no waiting on a friend to figure out invite links.
Three weeks later there’s a working product at www.wordyroyale.com: players putting coins on the line, a daily reward that hands out coins once a day, and an operator panel we hadn’t planned to build this early.

What we built
Wordy Royale is built to host a lot of games. Right now it hosts one. The lobby is a grid: rows are game types, columns are game modes. That grid currently has one game type live (Word Search) across a small set of tables, but the platform underneath doesn’t know or care that it’s only one — a new game type is a data row and a package, not a rewrite.
Pick a table, and if it has an entry fee, you pay it in coins. Every coin is play-money: you earn it by playing, and there’s no way to buy one with real currency. The ledger still has to be real, even though the money isn’t. Wait for enough players to arrive, then play. Win, and you split the table’s prize with whoever else made it to the top. There’s also a free table, tagged “Just the glory.” in the lobby, no entry fee and no coin prize, for anyone who’d rather skip the stakes. And there’s a daily reward: a small coin grant, once a day, just for showing up and playing a game.

The interesting part is what it takes to make “pick a table and play” trustworthy when coins change hands.
The stack, in one paragraph
We split truth into two owners. Postgres, a regular relational database, is the arbiter of money and identity: every coin that moves, moves as an append-only ledger entry, never a number we edit in place. Colyseus, a real-time framework built for exactly this kind of game, is the arbiter of the current moment of play: whose turn it is, what’s on the board, who’s still connected. Coins never live inside a game room’s memory, and a game room’s state never has to survive a server restart. Each side does one job and neither has to pretend to do the other’s. The API is a Node/Fastify service handling logins, the lobby, and admin actions; the player app is a React single-page app; everything runs on Railway, with a staging environment that’s a mirror of production and gets everything first.
Four things that broke
The parts of a build worth writing about are rarely the parts that went smoothly. Here are four that didn’t, and what we changed because of them.
The favicon that lied. Early on we shipped a new app icon. It built cleanly, deployed cleanly, and the server returned it with a 200 status code and the right number of bytes every time anyone asked for it. It also didn’t render: the file was malformed XML that no build tool caught, so every browser just showed nothing where the icon should be. We only found it by actually looking at the picture. Now any change to a player-visible image gets a look, not just a status check. Served correctly and displays correctly are two different claims.
The word list nobody reviewed. Our Word Search game needs a pool of words to build boards from. Early in development, we imported one — nearly twelve thousand words — and it passed every technical check: it loaded, it built valid boards, the game played fine. It also contained words that had no business being in a game aimed at a general audience, because it had only been tested for whether the code could use it, not whether it should. We found out when a player got dealt one of them on a real table. We pulled the words and re-seeded the pool the same day, and now any player-visible content gets a human content review before it ships, not just a passing test suite.
The container that worked everywhere except where it mattered. One of our internal admin tools builds as a Docker container. It built fine on a laptop and it built fine in CI. It failed the first time we tried to actually deploy it, because the way our hosting platform builds a container (from the root of the whole project) is different from the way a quick local build does it (from just that tool’s own folder): a single file path that resolved correctly in one context pointed nowhere in the other. The fix was small. “It built” only counts as proof if it built the way the real deploy builds it.
The fifteen-minute wait that was working as designed. Rooms in Wordy Royale are supposed to recycle: when one empties out or finishes, a fresh one should be ready to go. For a while, that recycling could take up to fifteen minutes, and from a player’s seat, a table that doesn’t refill for fifteen minutes just looks broken. Nothing was malfunctioning; the system was doing exactly what we’d told it to do. We rebuilt the turnover anyway, and a finished or emptied table is now ready again in about six seconds. That wasn’t a bug.
Why a game with an economy needs an operator
Once coins are on the line, “the code works” stops being the whole story. Someone needs to be able to see what’s happening, fix a stuck situation, and answer for any change that touches a player’s balance — and we needed that ability provable, not just possible.
So every table’s rules (entry fee, prize, player count, timing) live in the database as a row we can edit, not a constant baked into the client app. Change it, and the lobby reflects it without a new release. And every action an operator takes, from adjusting a table’s numbers to crediting a player, suspending an account, or force-closing a stuck game, writes an audit entry in the same database transaction as the change itself. Not as a nice-to-have logging step bolted on afterward: if the change doesn’t get recorded, the change doesn’t happen. Force-closing a table, for instance, refunds every player in that same atomic step before the room is torn down. There’s no window where a table disappears and the money doesn’t come back.
That operator surface — we ended up calling it the backoffice — runs as its own separate app, on its own address, reachable only over a private network we control. None of its code, and none of its routes, ship inside the app players use.

The tooling tour
A few of the pieces that don’t show up in the game itself but do a lot of the work of keeping it trustworthy:
Analytics we host ourselves. We run Umami, an open-source analytics tool, on our own infrastructure rather than sending visitor data to a third party. It answers the boring-but-essential questions — how many people showed up, where from, what they did — without a tracking script that follows anyone off the site.

Direct database access, kept narrow. Every so often the fastest way to answer a question is to look straight at the data. We run Adminer, a lightweight database browser, but it’s not exposed publicly — it’s only reachable over a private tailnet, a kind of VPN that connects specific trusted machines to each other and nothing else. Anyone browsing the database is doing it from a device that’s specifically on that network, not from a public URL.

Staging before production, every time. Every change lands on a staging environment first — a full mirror of production, minus real players — before it’s ever eligible to go live. Nothing reaches production without a deliberate, explicit decision to promote it there.
QA that doesn’t know why the code changed. Before anything ships, someone tests it without reading the reasoning behind the change — just the spec for what it’s supposed to do. That distance matters: it’s much easier to notice something’s broken when you’re not the person who just finished explaining to yourself why it’s fine.
What’s next
No roadmap here — we’d rather under-promise than walk something back. What’s true today: the core loop is live, it’s handling coins, and it’s been tested by people who didn’t build it. There’s more we’re chewing on for how players earn and spend what they win, but nothing specific enough yet to put a name to.
If you want to see where it landed, the lobby’s open at www.wordyroyale.com.
Next step
Curious about the process behind this — the same blind QA and human-approved promotions that got Wordy Royale here? How We Shipped an AI-Literacy Game in About a Day is the build story for a different product, built the same way.