Relay
A template for daily web games whose primitive is a link you can paste into a group chat.
Distilled out of about forty-five of them. It makes one game — run it once for something you keep for years, or once a week. Nothing in it assumes you are making more than this one.
The primitive
A link that unfurls into something worth tapping, opens straight into today's puzzle, and hands back a new link when you finish. Everything else exists to make that one line good.
Why the card is the whole thing
Nearly every game this was distilled from shipped one static image with the production URL hardcoded into the page. The first card tells a chat that a game exists. The second is a scoreboard nobody has to open, and it is the reason the next person taps.
Relay renders it per link, server-side, from a token in the URL — no database lookup, so it works for practice runs, private crews and old days too. Zero dependencies: there is a PNG encoder and a bitmap face in the repo.
Built in
- CrewsSix characters in the query string salt the daily seed, so a group chat gets a puzzle nobody outside it has seen. No rooms, no lobbies, no realtime.
- Practice gateA first-time player gets one throwaway round before their first daily, and is told it did not count.
- Verified runsThe client submits the moves it made, not the score. The server replays them through the same rules file.
- Leaderboard & streaksPer day, per crew, ranked by whichever metric you declare.
- ArchiveAny past day of your game is still playable, so a link from six weeks ago still opens something.
- Anonymous by defaultNo sign-in. A name is asked for once, after it has already earned a rank, and can be declined.
- ThemesA theme is a folder. Same ruleset, different game to the person playing it.
- Offline tolerantFully playable with the server down; the score just does not post.
Mobile or laptop
Mobile first, laptop respectable. The mechanism that makes that one build rather than two is a fixed portrait stage — a 10:16 box, identical on every device. On a phone it is the screen. On a laptop it is a framed play surface with the board and the countdown beside it, rather than a phone screenshot stranded in grey. Because the box is the same everywhere, what was on screen is the same rectangle for every player, which is what makes a daily fair.
If it needs a keyboard, it is not a link game.
It questions its own assumptions
Every game built on the template carries an assumption ledger, and npm test fails while any of it is unanswered. Five are checked by machine — by playing your game at random: does every run actually end, does the leaderboard metric ever vary, is the share strip becoming a fingerprint for the answer, is the rules file still free of the clock, does the tutorial seed collide with a real day.
The rest need a sentence from you. The one that gets the laziest answers is what makes day 30 different from day 3.
When one turns out to be wrong, one command flips it and opens a lesson stub with the assumption and the reality already filled in — because a broken assumption is the lesson, and that is the only moment it is cheap to write down. Finished lessons get promoted back into the template, attributed and dated, so the next game starts from them.
Starting one
node tools/new-game.mjs sixes "Sixes" ~/games/sixes # then: answer ASSUMPTIONS.md, write two files, npm test
Node 20 or later. No dependencies, no build step — the browser imports the same modules the server does. Deploys to Railway as it stands.