A 2.5D multiplayer world for creators and their communities, with an AI scribe that turns a PDF into a full course.
MVP shipped in six phases, then eight more
for content creators who want a world, not a Discord, for their community
Live multiplayer town square, taverns, coworking tents, an academy with real courses, and the Scribe: an AI course maker that drafts outline, lessons, and images from uploaded material across four approvable stages.
Outcome: reported by the owner.
- Monorepo: Next.js web app on Vercel, Colyseus game server on Railway, shared package for protocol types.
- Rooms auto-shard at 20 clients and filter by building, so three taverns with the same art stay socially distinct.
- The Scribe streams Gemini output in four stages (outline, lessons, images, seal) and only writes real course rows on seal.
- An AI guide NPC shipped in Phase 11 and was retired in Phase 13 for a static welcome: zero recurring cost, ADR recorded.
- Every phase has an ADR and a changelog; 298 commits with the decision trail intact.
![]() Creator studio in the midnight-scriptorium design system |
![]() The kit: live design-system spec with every token |
π MVP shipped 2026-04-21; post-MVP feature work continued 2026-04-22 β 25. The original 6-phase build (0β5) in /docs/mvp/phase-plan.md is verified on prod. Eight extension phases shipped on top: world rendering rebuild (image-backed), Phase 8 UI wire-up, Phase 9 feed + events, Phase 10 AI scribe, Phase 11 sage + UI legibility audit (the AI portion was retired in Phase 13 in favour of a static welcome β see ADR 0017), Phase 12.A coworking productivity, Phase 13 sage as static welcome, Phase 14 persistent player HUD. 15 of 17 Phase-5 steps landed in code; the two remaining (60 FPS measurement + demo-cut rehearsal) are manual QA before the demo recording.
What's live at arcadia-web-swart.vercel.app:
/β role-aware hub (World / Market / Dashboard)./worldβ image-backed town square, Colyseus-synced onworld-realm1(auto-shards at 20 clients). Walk off any edge to the neighbour: Nβ/academy-outside, Eβ/tavern-outside, Sβ/market, Wβ/coworking. Capacity HUD top-right. (2026-04-22: replaced the ADR-0007 Tiled square with this image-backed scene.)/academy-outsideβ single-player outdoor area. Walk up to the gate, press ENTER β/academy./tavern-outsideβ single-player outdoor area. Three distinct tavern doors (The Three Ravens / The Iron Chalice / The Sleeping Hollow); ENTER opens the matching tavern (?b=tavern-a/b/c)./coworkingβ single-player outdoor area. Five tents; ENTER enters the matching tent (?b=tent-1..5)./coworking/inside?b=<id>β tent interior, multiplayer. Colyseuscoworking-realm1withfilterBy(['building'])β members in different tents never meet even with same interior art. Auto-shards at 20 clients./tavern?b=<id>β image-backed bar, Tab-to-chat with speech bubbles, live XP leaderboard. Same interior visuals across all three tavern buildings;filterBy(['building'])keeps the social spaces distinct./academyβ Phaser course hall with walkable podiums./academy/[courseId]β YouTube IFrame Player (resume + 80% completion) +react-markdownlessons (scroll-to-complete)./marketβ Phaser interior with a central crystal. Walk to the crystal + ENTER β fullscreen four-stall dashboard overlay (Courses Β· Templates Β· Tools Β· Exclusives). Picker is a 3-up top row + a wide gilt-haloed Exclusives card on the bottom. Courses are real DB rows (published + same-realm via RLS) and use the existingenrolInCourseserver action. Templates / Tools / Exclusives are simulated fixtures (mix of free + priced); ownership is in-memory, no real payments. Pricing displays as$X. Overlay header is just β close (closing returns the player to the Phaser scene; logout lives on the dashboards). See PR #59β#61 changelog trail./dashboardβ creator studio (role-gated). 6 tabs: studio Β· courses Β· events Β· folk Β· payouts Β· settings. Scriptorium-styled per Phase 8./dashboard/courses/[id]β drag-reorder section/lesson tree + TipTap WYSIWYG lesson editor (Phase 10) + YouTube editor + publish toggle + Analytics pill. Replaced the old@uiw/react-md-editorsplit-pane./dashboard/courses/conjure(Phase 10 β the Scribe) β staged AI course maker. Satchel accepts PDFs/DOCX/TXT/MD as grounding context; Gemini drafts outline β lesson bodies β images across four approvable streaming stages; seal materializes real courses + sections + lessons rows. Per-creator "scribe's memory" (voice, image style, audience) persists across drafts./dashboard/events(Phase 9) β schedule + manage live events. KPI strip + live/upcoming/past sections; events drive the tavern's "live now" banner + YouTube stage overlay./dashboard/courses/[id]/analyticsβ enrolment count, completion rate, active-in-7d, recent activity.- The Wanderer (
/world) (Phase 11 β revised Phase 13) β bearded merchant on the rug in the top-left of the square. Walk near him + ENTER β "welcome, traveller" panel + 4 flip cards (Academy / Square / Tavern / Coworking β Tavern replaced Dashboard in PR #39 as the more member-relevant surface). Static; no AI calls (ADR 0017 supersedes 0014). - Tavern feed (Phase 9) β tablet on the back wall of every tavern; ENTER opens an async feed showing creator posts + scheduled events. Live events get a verdigris banner + stage embed.
- Gamification β lesson completion β +25 XP via DB trigger β level recomputes β banner animates β peer badges sync via Colyseus
UPDATE_LEVEL.
Video host is YouTube unlisted (ADR 0006, demo-only scope β swap to a real host required before paying creators).
Beyond the MVP plan:
- 2026-04-22 (morning): world swap to orthogonal top-down Tiled square (ADR 0007 implemented; see changelog).
- 2026-04-22 (evening): image-backed world + 3 outdoor neighbour scenes + per-building Colyseus sharding + capacity HUD (see changelog).
- 2026-04-23: shipped to prod with extensive polish β ENTER-key gates, archway-proximity exits, Georgia-serif nameplate, door-aware returns, zoom/walk-speed tuning, Phaser physics-body fix (ADR 0008). Full write-up in changelog.
- 2026-04-24: Phase 8 Β· UI wire-up β every React surface rebuilt on the midnight-scriptorium design system (tokens + primitives + simulation toggle + 26-item kit-backfill pass across dashboard / academy / market / login / landing). See changelog. Public
/kitrenders the live design system;/preview/*routes render every dashboard tab against fixtures for design review without a session. - 2026-04-24: Phase 9 Β· async feed + live events β tablet-triggered scriptorium feed in the tavern,
/dashboard/eventsmanagement tab, verdigris "live now" banner + YouTube stage overlay when an event is live in-shard, upcoming-event pill on/+ studio. Migration20260424000001_phase9_feed_and_events.sqlapplied to prod. See changelog. - 2026-04-25: Phase 10 Β· the scribe (AI course maker) β new
/dashboard/courses/conjureworkbench. Satchel accepts PDF/DOCX/TXT/MD grounding material; Gemini drafts outline β lesson bodies β images across four approvable streaming stages with inline re-roll feedback; seal materializes a real courses/sections/lessons tree. Per-creator "scribe's memory" (voice, image style, audience) injected into every prompt. Bundled with a TipTap WYSIWYG replacement for the lesson editor (@uiw/react-md-editorretired). Wired through@ai-sdk/googledirectly (Gemini 2.5 Pro/Flash + Imagen 4 Fast, ADR 0013 supersedes ADR 0011's Gateway pick). Three migrations applied to prod. See changelog. - 2026-04-25: Phase 11 Β· the Sage β the bearded merchant in the upper-left of the square is now an AI guide. Walk near him + ENTER β scriptorium chat popup. Knowledge corpus baked from
/docs/mvp+ every ADR + README + recent changelog (no RAG, ADR 0014). Conversation persists in localStorage. Bundled with a deep UI legibility audit. See changelog. - 2026-04-25: Phase 12.A Β· coworking productivity β five surfaces wired to objects already drawn into the tent PNG. Jukebox (right wall, shared station, per-tab volume + 4 ambient tracks). Hourglass (wooden chest, shared Pomodoro with server-clock ticker). Hearth pill (top-right, room occupancy + in-flow count). Focus pill (top-left, "what I'm working on" line that renders above the avatar's nameplate everywhere; per-avatar state via
currentFocusonAvatarState). Pomodoro banner (top-centre, lantern β verdigris on phase change). Per-tent state on the Colyseus room; newJukeboxState+PomodoroStateschemas + 4 protocol messages (SET_JUKEBOX,START_POMODORO,STOP_POMODORO,SET_FOCUS). ADR 0016 captures the hand-pick-coords-over-PNG pattern. See changelog. - 2026-04-25: Phase 13 Β· sage as static welcome β the AI Sage (Phase 11) was retired the same week it shipped. The bearded merchant's popup is now a static "welcome, traveller" panel + four flip cards (Academy, Square, Tavern, Coworking β PR #39 swapped Dashboard β Tavern as the more member-relevant surface). Front of each card: sigil + label + tagline; back: 2β3 sentences + a navigation hint. Click / Enter / Space flips. Zero recurring API cost; the Scribe's Gemini wiring is untouched and still powers
/dashboard/courses/conjure. ADR 0017 supersedes ADR 0014. See changelog. - 2026-04-25: Phase 14 Β· persistent player HUD β the player's identity + menu shortcuts follow them across every Phaser scene. One full-width bar at the top: heraldic SVG shield (level number) + display name in JetBrains Mono 600/17 + XP progress bar (current β next-level) + five placeholder menu icons (Profile / Chat / Quests / Events / Settings). Dark brown panel (
--inkat 85% alpha) with a bronze 9-slicedborder-imagefrom a Kenney "Fantasy UI Borders" PNG (CC0, recoloured cream β bronze viascripts/tint-panel.mjsat Ξ± 0.55 for a soft rim). The bar lives in the page's flex flow (not a fixed overlay) so the Phaser canvas claimsflex-1below it β world art and the avatar can't render behind it; the bar also stays hidden during scene preload (loaded={sceneReady}βvisibility: hiddenuntil ready). Modals at z-index 80 still obscure the HUD. The local player's in-world nameplate is hidden (the HUD shows the same info); remote peers' nameplates render as a small circular bronze-rimmed Arc badge with the gilt level digit on the left + the display name in mono on the right. Initial ship + twelve polish iterations (PR #30 β #45) bundled in changelog; ADR 0018 records the rendering choices. - 2026-04-26: Phase 14 Β· post-ship wiring (PR #48 β #57) β second polish wave on the same HUD. The bar's right-side row of icons grew into a real three-section layout: left = shield + name + XP, middle = location title + live
(N wandering)occupants count, right = role-aware icons. Right section is now role-aware: members see Profile / Chat / Quests / Events / Settings (still placeholders), creators + admins see Courses / Events / Members / Billing / Settings. Creator icons open the matching dashboard tab as an in-world iframe overlay (DashboardOverlay) instead of route-changing β the persistent ambient music + Phaser canvas + Colyseus connection never tear down. Bundled with PR #56's persistent ambient music (Woven_Paths_at_Nightfall.mp3mounted as a sibling of{children}in the root layout, gated silent on/login//signup//onboarding/*), PR #55's dashboard tab rename (folkβ "members",payoutsβ "billing"; routes unchanged), PR #50's single-session-per-member server fix (kicks old tab on new join with close code 4001), and PR #54's tavern feed refetch so the poster sees their own post. See changelog. - 2026-05-02: Phase 14 Β· market rework + HUD/auth polish (PR #58 β #61) β Four follow-up PRs on top of the post-ship wiring. PR #58 rewires the dashboard "exit" button to a real signout form (labelled logout), banks autoplay permission inside the login submit handler, and prewarms a hidden
/dashboardiframe onrequestIdleCallbackso the first creator-icon click drops from ~600β1500 ms to ~50β200 ms. PR #59 + #60 rework/market: it routes back to the PhaserMarketScene; walking to the central crystal + ENTER opens a fullscreen four-stall dashboard (Courses Β· Templates Β· Tools Β· Exclusives) as an in-worldMarketOverlayover the canvas (same page, not iframe β keeps audio + canvas alive). Courses are real DB rows; Templates / Tools / Exclusives are simulated fixtures with$Xpricing and a 700 ms "stampingβ¦" purchase flip. PR #61 is three rounds of polish on top: dashboard β return-to-world removed (logout-only); MarketOverlay header trimmed to just β close (the path-aware β world / logout /?from=squareplumbing was rolled back); CategoryPicker grew a 3-up + 1-wide layout with a thicker pulsing gilt halo on Exclusives + responsive grid; CategoryView item rows tightened (compact PriceTag pill instead of the heavy Chip); AmbientMusic rewritten as a YouTube-pattern single-state machine β no more<audio autoPlay>racing JS, no more retry-on-window-pointerdown that missed iframe clicks, no more pre-hydration audible flash. The legacyCatalogScroll+?course=<id>-drivenStallVieware preserved on disk for reference. See PR #59β#60 changelog + PR #61 changelog. - 2026-05-06: Demo NPCs + simulation toggle pill (PR #62) β autonomous wandering NPCs populate every Phaser scene when simulation mode is on. New shared
NpcSwarm(scenes/shared/npc-swarm.ts) reuses the existingavatar-renderer+avatar-animationsso each NPC reads identical to a real peer (same sprite, nameplate with random level 1β12, walk anims). State machine: walking β arrive β idle 1β4 s β repeat; per-NPC speech bubbles every 5β14 s pulled from a 30-line ambient pool. Speech bubble factory extracted to a sharedscenes/shared/speech-bubble.tsso the player tavern chat + NPC mutterings render identically; bubble visuals also bumped (font 14β18 px, max-width 200β290 px). NPCs total ~36 across the 8 scenes (Square 6, Market/Academy/Tavern/Coworking-inside 5 each, three outdoor scenes 5 each via the shared base). New<SimulationPill />component (bottom-left, always-visible, off=outline / on=lantern fill) mounted inapp/layout.tsxas a sibling of<AmbientMusic />β flipping it spawns/despawns the swarm immediately via the newSIM_CHANGE_EVENTwindow event. Pill hides itself inside iframes (so the in-world DashboardOverlay's iframe doesn't render its own pill on top of the dashboard) and sits at z-70 (below the overlay backdrop at z-80). The redundantSimulationBadgemount was removed fromDashboardShellβ the inline<SimulationToggle />in the header is the canonical sim control inside the dashboard view. ADR 0010 amended. See changelog. - 2026-04-25: Hotfix Β· server room bounds aligned with image-backed world β peers were seeing each other at wrong positions in
/worldand/tavernbecause the server'sroom-config.tswas never updated when those routes swapped to image-backed scenes on 2026-04-22. Server'sapplyMovewas silently clamping incoming MOVE coords to the old iso bounds. Snapped server bounds + spawn to match the client image dimensions (square0..2508, tavern0..1536 / 0..1024); coworking was correct already. Railway must redeploy@arcadia/game-serverfor it to land in prod. See hotfix changelog. - Polish (collider rects, 60 FPS / demo-cut passes, production hardening) tracked in
phases/phase-02_polish_backlog.md+phases/phase-05_status.md.
Phase exit logs: Phase 3 Β· Phase 4 Β· Phase 5 Β· Phase 8 Β· Phase 9 Β· Phase 10 Β· Phase 11 Β· Phase 12.A Β· Phase 13 Β· Phase 14 Β· Phase 14 post-ship wiring Β· Market rework Β· Market + audio polish Β· Demo NPCs + sim pill. Creator-flow setup walkthrough in docs/guides/phase-03-setup.md; workspace scoping cheat sheet in docs/claude-scoping.md.
| Service | URL |
|---|---|
| Web (Next.js, Vercel) | https://arcadia-web-swart.vercel.app |
| Isometric spike (Phaser 3.88) | https://arcadia-web-swart.vercel.app/spike |
| Game server (Colyseus, Railway) | wss://arcadia-production-c635.up.railway.app |
| Game-server health | https://arcadia-production-c635.up.railway.app/health |
| Supabase (production) | https://eqbzltiasmuckgsapkye.supabase.co |
| Supabase (test β cross-member leakage harness) | https://idxgcrwikmcuqrrxbogj.supabase.co |
docs/mvp/prd.mdβ product requirements (MVP v1.1)docs/mvp/tad.mdβ technical architecture (MVP v1.1)docs/mvp/phase-plan.mdβ 12-week phased build (MVP v1.1)docs/art/sprite-requirements.mdβ full art-order specplanning/decisions/β architecture decision records (ADRs)CLAUDE.mdβ workspace routing, conventions, tooling
Next.js 14 on Vercel Β· Phaser 3.88 Β· Colyseus 0.16 on Railway (Redis) Β· Supabase (Postgres + RLS + Auth + Realtime + Storage) Β· YouTube unlisted (MVP demo video host, per ADR 0006 β CF Stream is the post-MVP target) Β· @dnd-kit Β· @uiw/react-md-editor Β· Tailwind 3 Β· TypeScript 5 Β· Tiled
Full rationale: planning/decisions/0001_2026-04-18_locked-stack.md.
apps/webβ Next.js client (Vercel)apps/game-serverβ Colyseus server (Railway)packages/sharedβ shared TypeScript types, protocol, constantsdocs/β canonical MVP docs, API reference, guides, changelog, art specplanning/β specs, architecture, ADRsphases/β phase plans and status logsops/β deploy, monitoring, scripts
Requires Node 22.12+ or Node 24+ (the game server hits require(ESM) via Colyseus's rou3 transitive dep, which only works on those versions β or on Node 22.x with --experimental-require-module, which the game-server's start script already passes). Local dev on Node 24 LTS via nvm use is the tested path; Railway production runs Node 22.11 with the flag.
npm install # installs all workspaces
npm run lint # ESLint across all workspaces
npm run typecheck # tsc --noEmit across all workspaces
npm run test # Vitest across all workspaces
npm run format # Prettier write across code filesPer-workspace dev commands (e.g. next dev, tsx watch) live in each apps/* and packages/* package.json.
Built by Muhammad Ibrahim Raheel Β· more work at ibiraheel.com

