Skip to content

Plan: justinkrystal.com games hub (Vite + Phaser + leaderboard)

URL: https://mkdocs.justinsforge.com/memory/plans/justinkrystal-games-hub-2026-07-02/

Date: 2026-07-02 Approved spec: A Vite + Phaser 3 + TypeScript games hub for justinkrystal.com. Static build output lands in sites/justinkrystal.com/landing/games/, served by the existing nginx container on media-server LXC 102. First game is Michael's birthday "Set the Hook" fishing reaction game (bass +3 / birthday cake +5 / rabid squirrel -2), emoji art. The birthday landing page at landing/michaels23rd/index.html (sticky banner + message + Immich photo gallery) embeds the game via iframe and is reachable at justinkrystal.com/michaels23rd. Backend leaderboard extends the existing Flask hub-api/app.py with a sqlite-backed store (POST /api/games/score, GET /api/games/leaderboard), HMAC-signed submissions plus server-side sanity caps and per-IP rate limiting for anti-cheat. Dev workflow uses the Vite dev server on Console LAN (192.168.86.50) with HMR. Deploy adds an npm run build step to deploy.sh before the existing tar-and-push. Out of scope: multiplayer/realtime, user accounts/login, custom (non-emoji) art, any game beyond fishing (hub is structured to add more, only fishing ships now).

Task list

Task 1: Scaffold the Vite + Phaser + TS project

  • Files: sites/justinkrystal.com/games-src/package.json, vite.config.ts, tsconfig.json, index.html, .gitignore, src/main.ts
  • What: Create a Vite project (vanilla-ts) with Phaser 3 dependency. vite.config.ts sets base: '/games/' and build.outDir to ../landing/games. Hub index page lists games.
  • Verification: cd sites/justinkrystal.com/games-src && npm install && npm run build && test -f ../landing/games/index.html && echo OK
  • Commit: feat(justinkrystal): scaffold Vite+Phaser games hub

Task 2: Build the "Set the Hook" fishing game (Phaser scene)

  • Files: sites/justinkrystal.com/games-src/src/fishing/FishingScene.ts, src/fishing/main.ts, sites/justinkrystal.com/games-src/fishing.html
  • What: Phaser scene: cast, randomized bite delay, dip + tap window, weighted catch (bass/cake/squirrel), scoring, too-soon miss, confetti on cake, best score in localStorage. fishing.html is the standalone entry the birthday page iframes.
  • Verification: cd sites/justinkrystal.com/games-src && npm run build && test -f ../landing/games/fishing.html && echo OK
  • Commit: feat(justinkrystal): Set the Hook fishing game

Task 3: Leaderboard backend (sqlite + Flask endpoints)

  • Files: sites/justinkrystal.com/hub-api/app.py
  • What: Add sqlite store at /data/leaderboard.db. POST /api/games/score (game, name, score, HMAC sig) with sanity cap + per-IP rate limit + name clamp; GET /api/games/leaderboard?game=fishing&limit=10 returns top N. Shared secret from HUB_SECRET_KEY.
  • Verification: cd sites/justinkrystal.com/hub-api && HUB_DATA_FILE=/tmp/hd.json python -c "import app; c=app.app.test_client(); print(c.get('/api/games/leaderboard?game=fishing').status_code)"
  • Commit: feat(justinkrystal): sqlite games leaderboard API

Task 4: Wire game to leaderboard + name entry

  • Files: sites/justinkrystal.com/games-src/src/fishing/FishingScene.ts, src/fishing/leaderboard.ts
  • What: On game over, prompt for name, POST signed score, fetch + render top 10. Graceful offline fallback to localStorage-only.
  • Verification: cd sites/justinkrystal.com/games-src && npm run build && grep -q leaderboard ../landing/games/fishing.html && echo OK (bundled ref present)
  • Commit: feat(justinkrystal): fishing game leaderboard integration

Task 5: Birthday landing page finalize + game embed

  • Files: sites/justinkrystal.com/landing/michaels23rd/index.html
  • What: Keep banner + message + gallery; replace the inline DOM game with an iframe to /games/fishing.html. Gallery reads a PHOTOS filename list from photos/.
  • Verification: curl -fsS http://127.0.0.1:8091/michaels23rd/ | grep -q 'games/fishing' && echo OK (preview server)
  • Commit: feat(justinkrystal): Michael 23rd birthday page with embedded game

Task 6: Deploy pipeline build step

  • Files: sites/justinkrystal.com/deploy.sh
  • What: Prepend cd games-src && npm ci && npm run build (guarded: skip if node absent, warn loud). hub-api rebuild already handled by compose. Add leaderboard.db to hub-data volume note.
  • Verification: bash -n sites/justinkrystal.com/deploy.sh && echo OK
  • Commit: chore(justinkrystal): deploy builds games bundle
  • Files: sites/justinkrystal.com/landing/michaels23rd/photos/*, landing/michaels23rd/index.html (PHOTOS array)
  • What: After Justin pastes Immich asset IDs, download originals via the Immich API over ssh immich, resize/convert to web jpg, drop in photos/, list filenames in the PHOTOS array.
  • Verification: ls sites/justinkrystal.com/landing/michaels23rd/photos/*.jpg | wc -l matches count Justin gave.
  • Commit: feat(justinkrystal): add Michael birthday photos

Task 8: Register and document

  • Files: MEMORY.md, memory/general/reference_justinkrystal_games_hub.md
  • What: Topic file documenting the games hub (paths, dev workflow, leaderboard API, deploy, how to add a game), plus MEMORY.md index line. Run eval harness.
  • Verification: bash scripts/forge_eval_run.sh
  • Commit: docs(memory): justinkrystal games hub reference