Analyzed 2026-07-04, at commit a6d592d ("organize websocket utils and listeners", 2024-03-11), branch master, remote git@github.com:snk-js/hyperchess.git.
A 3D chess game built with SvelteKit 1 / Svelte 4 (not Svelte 5), rendered with
Threlte 7 (Three.js for Svelte) plus Rapier physics, postprocessing effects
(glow/glitch), Tailwind + Skeleton UI. It has working local gameplay, a full
username/password auth flow, and a partially-finished multiplayer lobby ("rooms")
that talks to the separate warp-websockets Rust server.
| Layer | Technology |
|---|---|
| Framework | SvelteKit ^1.27, Svelte ^4.2, Vite 4, TypeScript 5 |
| 3D | Threlte 7 (@threlte/core, extras, rapier, theatre), three 0.153, postprocessing |
| UI | Tailwind 3, Skeleton labs, svelte-loading-spinners |
| Auth | Lucia v2 + @lucia-auth/adapter-prisma |
| DB | PostgreSQL via Prisma 5 |
| Prod server | server.js — Express wrapping the adapter-node build, port 3000 |
| Package manager | pnpm (engine-strict=true in .npmrc) |
- Aug–Sep 2023 — core game: board as writable cells (perf refactor), piece moves (bishop/knight fixes), hover/highlight, glow effects, gh-pages static deploy.
- Oct 2023 — switch to pnpm, Tailwind, WebSocket API skeleton; Prisma +
Lucia auth added and finished (
641bbe7schema →bab825b"finish lucia auth"). - Nov 2023 — rooms: Redis tried and removed (
31cf487→40c72ab), replaced by direct WebSocket connection to the Rust server; create-room via server actions; production build adjustments (dockerfile era). - Dec 2023 — loading states, waiting-for-player UI, camera smoothing, infinite room-creation bugfix.
- Feb–Mar 2024 — multiplayer sync push: session ids, user syncing ("nice progress on syncing users"), topic-based pub/sub API, ws utils reorganization. Work stops mid-feature here.
Branches feature/lucia-auth and feature/integrate-websocket-with-rust-server
are both fully merged into master (no unmerged commits). gh-pages holds the old
static demo deploy. Working tree is clean.
- Schema:
auth_user,auth_session,auth_key(classic Lucia v2 trio), one migration20231023160407_init. Postgres connection viaDATABASE_URLenv. src/lib/server/lucia.ts— Lucia configured with the Prisma adapter,debugMode: truestill on.src/hooks.server.ts— attacheslocals.authper request; also appends permissive CORS headers (*) on/apiroutes.signup/+page.server.ts— creates user + key (usernameprovider) + session.login/+page.server.ts—auth.useKey('username', ...), creates session and manually sets a secondauth_sessioncookie withsecure: false(commented "prod: disable this").api/logout/+server.ts— invalidates the session.postgres.md— manual local Postgres bootstrap notes (has a typo:pqsl; and mixes placeholder namesmyuser/hyperchessuser).
Flow as implemented today:
+page.server.tsload: validates session, fetches user from Prisma, then server-side callsapi/ws→ Rust/registerwithuser_id = getDigitsFromString(uuid)and topicROOMS, returningwsUrl+clientIdto the page.+page.sveltecallsregisterClient('ROOMS', roomsEventHandler, user)which registers again from the browser and opens the WebSocket to the returnedws://127.0.0.1:8000/ws/<client_id>URL.- Creating a room: form action builds a
RoomPayload(id =Date.now()), then the client publishes it on topicROOMSviaapi/publish→ Rust/publish; all subscribed clients add the room to their table. api/add/api/removeproxyadd_topic/remove_topic— used to join/leave aMATCH-style topic.roomsEventHandleradds rooms to the store and flips the sender intoplaying: true.
What's missing / broken:
- No actual game-move sync: topics exist (
'ROOMS' | 'MATCH' | string) but there is no handler wiring board moves over theMATCHtopic; the Feb–Mar 2024 commits were mid-way through this. - Rooms are ephemeral & in-memory only — a page refresh loses the lobby; no persistence of rooms or games in Postgres (only auth tables exist).
getDigitsFromString(uuid)maps user UUIDs to a number by stripping non-digits — collision-prone and lossy; falls back toMath.random()when empty.- Rust server URL
http://localhost:8000is hardcoded in 4 API route files (api/ws,api/publish,api/add,api/remove). - No server-side move validation (explicitly listed as TODO in the README).
- Double registration (server load + client) can leak dangling clients in the Rust server's map.
- Board logic lives client-side:
src/lib/utils/moves.ts(99 lines),directions.ts(133 lines), storescellStates,turn,main. - Pieces are individual Svelte components per color/type under
components/piece/pieceClasses/. scene-transformed.glb(4.5 MB) sits in the repo root;scripts/model-pipeline.jsconverts GLB → Threlte components via@threlte/gltf.- README's own status: "piece replacement is instable", "needs websockets to connect two players", "needs to validate move on the server".
dockerfile— two-stage Node build: pnpm install →prisma generate→vite build, runtime = build output + prod deps +server.js,EXPOSE 3000. Works, but usesnode:latest(unpinned) and doesn't run migrations.server.js— Express + wildcard CORS +/healthcheck+ SvelteKit handler.svelte.config.js— adapter is inverted-looking:NODE_ENV === 'development'→adapter-static, otherwiseadapter-node. This was for the gh-pages static demo; confusing and worth cleaning up. Alsocsrf.checkOrigin: false..github/workflows/static.yml— GitHub Pages deploy, triggers only on thegh-pagesbranch (legacy static demo, pre-auth).- No
.env.example—DATABASE_URLis the only required var but is undocumented outsidepostgres.md. - No tests of any kind, no CI for lint/check.