checkmate.blue Roadmap#
High Priority#
Time Controls#
Chess clocks with configurable time + increment. The lexicon already has optional time control fields.
- Bullet (1+0, 2+1), Blitz (3+0, 3+2, 5+0, 5+3), Rapid (10+0, 15+10), Classical (30+0)
- Clock display on the game page with countdown timers for each player
- Timeout detection: if a player's clock runs out, flag the game as lost on time
- Challenge: No server to enforce clocks. Both clients track time independently using
lastMoveAttimestamps from PDS records. Drift is possible but acceptable for casual play -- the bot (once deployed) could serve as an authoritative time arbiter for disputed games.
Client-Side Integrity Checks#
Detect tampered or invalid game records from the opponent and halt the game gracefully.
- When applying an opponent's PGN, verify the new move history starts with the exact sequence we already have (no rewritten history)
- Reject PGNs with illegal moves (chess.js
loadPgnfailure) - On mismatch, show a "Game state mismatch detected" banner and disable further moves
- No PDS writes about corruption -- purely client-side detection and UI
Homepage & Game Discovery (Phase 4)#
Makes the platform feel alive and gives new visitors something to see.
Active games list: Show ongoing games on the homepage sorted by most recent activity. Only canonical records (no parentGameUri). Display as cards with player handles, move count, last activity, and a link to spectate.
Completed games feed: Recently finished games with results, player handles, and links to view the final position.
Blocker: Depends on Constellation supporting global collection queries. Current options if that lands:
- Query Constellation for all
blue.checkmate.gamerecords globally - Filter to
status: active(orcompleted) withparentGameUriabsent - Sort by
lastMoveAt
Without global queries, the alternatives are a known-players list (doesn't scale), a Jetstream firehose client-side index (complex, ephemeral), or a lightweight indexing endpoint (breaks the static SPA constraint).
Open Challenge Board (Phase 7)#
Browse and accept open challenges from anyone, not just targeted invites.
- Public list of open challenges on the homepage via Constellation
- Filter out expired challenges client-side (older than 24h)
- Same blocker as Phase 4 -- needs Constellation global query support
Medium Priority#
Lazy-Load Auth / Reduce Bundle Size#
The @atproto/api + OAuth bundle is ~854 KB (169 KB gzipped) and loads for every visitor including spectators. Dynamically import the auth module only when the user clicks "Sign in" so unauthenticated visitors never pay the cost.
- Wrap
createOAuthClientandauth.initbehind a dynamicimport() - Spectators and first-time visitors see the page instantly without loading the AT Protocol SDK
- Authenticated users load the SDK on demand (cached after first load)
Push Notifications#
Browser Notification API for turn alerts, especially useful when both players aren't staring at the screen.
- Request notification permission on game join
- Notify when it's your turn (opponent made a move)
- Notify on rematch offers, challenge invitations
- Respect user preference (opt-in, stored in
localStorage)
Keyboard Move Input#
Type moves in algebraic notation (e.g., "e4", "Nf3") instead of dragging pieces.
- Input field on the game page, submit with Enter
- Validate against legal moves via chess.js
- Accessibility win -- screen reader friendly
- Could support UCI notation too (e.g., "e2e4")
Rematch Timeout#
Auto-cleanup when a rematch goes unanswered.
- After creating a rematch game, start a 5-minute countdown
- If no opponent joins within 5 minutes, delete the waiting record via
deleteRecordand redirect to the homepage - Opponent's "Dismiss" hides the notification on their end (no protocol change needed)
Orphaned Game Cleanup#
Let users clean up stale games that were never accepted or finished.
- On the homepage or a "My Games" section, show the user's own records stuck in
waitingstatus - Allow one-click deletion of orphaned records via
deleteRecord - Consider auto-prompting on game pages: if a
waitinggame is older than 24 hours, offer to delete it
Chess Variants#
Support for non-standard starting positions. Add a variant field to the blue.checkmate.game lexicon and store the starting FEN in the record.
Chess960 / Fischer Random: Randomized back-rank starting positions (960 possible arrangements). Eliminates opening theory. chess.js supports it natively. Store position number (1-960) in the record. Castling rules differ slightly (king/rook end on standard squares regardless of start position).
Daily Really Bad Chess: Each day, a deterministic seed generates a random board with non-standard piece composition (asymmetric -- each side gets different random pieces). All players get the same board, play against the bot, compare results. Wordle-style daily challenge.
- chess.js handles arbitrary FENs and generates legal moves per standard piece movement rules
- Stockfish plays from any FEN position -- evaluation is tuned for standard chess but move generation is correct at any depth
- Disable castling in the FEN for non-standard boards (king/rooks won't be in expected squares)
- Seed generation: hash the date string to get a deterministic board, store the FEN in the game record
- Leaderboard: fewest moves to checkmate, or fastest time (requires time tracking)
Lower Priority#
Board and Piece Themes#
Chessground supports multiple board and piece styles. Let players customize their experience.
- Board themes: brown, blue, green, wood, marble, etc.
- Piece sets: cburnett (current default), merida, alpha, Leipzig, etc.
- Store preference in
localStorage - Theme picker in a settings menu or on the game page
Player Profiles#
/profile/{handle} page showing a player's game history and record.
- List completed games with results, opponents, and links to view
- Win/loss/draw stats (queried from the player's own PDS records)
- Without the bot's index, limited to games discoverable from the player's own repo
Follow-Graph Challenges#
Use the Bluesky social graph to suggest opponents.
- "Challenge someone you follow" -- fetch the user's follow list, display as challenge targets
- Shows who's online or recently active on checkmate.blue (if discoverable)
- More social than typing in a handle manually
Puzzles#
Daily tactics puzzles from a public database (Lichess has a CC-licensed set with millions of puzzles).
- No opponent needed -- good for engagement when nobody's online
- Random puzzle or daily puzzle (same for everyone)
- Track solve streak in
localStorage
In-Game Chat#
Real-time messaging between players during a game.
- ATmosphere-native approach:
blue.checkmate.messagerecord collection. Each player writes messages to their own PDS, opponent reads via Jetstream. Same pattern as moves. Each player owns their messages. - Downside: adds a new lexicon, more PDS writes, Jetstream latency
- Alternative: link to a Bluesky thread as a public "game lobby"
Premoves / Move List Panel / Takebacks#
Quality-of-life features for the playing experience.
- Premoves: queue a move while waiting for opponent (chessground supports natively)
- Move list panel: algebraic notation alongside the board, clickable to step through positions
- Takebacks: same pattern as draw offers -- request/accept/decline via a record field
Opening Name Display / Board Coordinates#
Minor polish.
- Show opening name (e.g., "Sicilian Defense, Najdorf") from the ECO database
- Toggle rank/file labels on the board (chessground supports this)
Dynamic OG Tags (Phase 2b)#
Per-route Open Graph meta tags for richer link previews when sharing game URLs.
- Cloudflare Worker intercepts requests from known bot user agents
- Fetches game/challenge data from the PDS
- Returns minimal HTML with correct
og:title,og:description,og:image - All other requests pass through to the static SPA
| Route | og:title | og:description |
|---|---|---|
/ |
checkmate.blue | Chess on the ATmosphere |
/game/{did}/{rkey} |
Chess Game - {white} vs {black} | {status} - {move count} moves |
/challenge/{did}/{rkey} |
Chess Challenge from {handle} | {handle} wants to play chess |
/profile/{handle} |
{handle} on checkmate.blue | Chess profile |
Blocker: Needs Cloudflare (or equivalent edge worker) set up in front of the static site.
Separate Project: checkmate.blue Bot#
Not part of this repo. A Jetstream listener service that watches blue.checkmate.game records and posts game results from a @checkmate.blue bot account. Separate repo, separate deployment, runs as a persistent process. See specs/bot.md for the full specification.
Responsibilities: game result posts, playable chess engine (3 difficulty levels), global game indexing with REST API, result verification, abandoned game detection.
Completed#
- Phase 1: Challenge & Invite Flow -- Post to Bluesky, DM via clipboard, color selection, check-before-join guard
- Phase 2a: Branding --
#checkmatewordmark, path-based favicon, PWA icons, OG image with composite board - Phase 3: Spectator Mode -- read-only view, unauthenticated viewing, dual Jetstream, game-full fallback
- Phase 5: Game Result Sharing -- editable share-to-Bluesky post with facets, grapheme counting, link card embed
- Phase 6: Polish -- sounds, rematch with live notification, PGN export, Lichess analysis, abandon detection, PWA
- Accessibility -- WCAG 2.1 AA (focus indicators, ARIA roles, reduced motion, route titles, contrast)