A realtime drawing-and-guessing adventure for fellowships, friends, and rival wizards.
Realtime rooms • Shared canvas • Persisted scores • AI rivals
Experience · Features · Bots · Architecture · Run locally
Mithril Tiles transforms the familiar draw-and-guess party game into a complete Middle-earth-inspired multiplayer experience. Create a private room, gather a company, choose a word pack, race against the round clock, and discover whose score will be carved into the final standings.
This is not a static frontend demonstration. It is a full game system with authenticated identities, guest access, realtime WebSockets, authoritative room state, cross-device canvas input, round rotation, bot participants, and PostgreSQL-backed results.
Watch the full gameplay showcase
Enter the Drawing Halls
A manuscript-inspired landing experience calls every fellowship to the game.
Then take command of the canvas
Draw, decipher, score, and survive every round together in realtime.
- Enter quickly. Create an account or choose a guest identity and reach the game without unnecessary friction.
- Gather the fellowship. Create a private room or join friends with a shareable room code.
- Shape the match. The host selects a word pack and can invite active bot profiles before starting.
- Draw and decipher. One participant receives the secret word while everyone else watches the shared canvas and submits guesses.
- Rotate and compete. The drawer changes between rounds, correct guesses earn authoritative points, and the game advances on the server.
- Reveal the victor. Completed rounds and final rankings are persisted and presented on the closing scoreboard.
| Each room coordinates chat, drawing strokes, guesses, timers, participants, and lifecycle events through a dedicated in-memory room actor. | The server assigns drawers, protects secret words, verifies drawing identity, evaluates guesses, calculates scores, and controls round transitions. |
| Mouse, touch, and pen input become normalized Canvas 2D strokes, keeping the drawing consistent across different viewport sizes. | Registered users and temporary guests share the same room model, while the frontend keeps backend credentials inside secure HttpOnly cookies. |
| Games, participants, rounds, round scores, and final rankings are persisted in PostgreSQL instead of disappearing with the socket connection. | The interface carries one visual language from the hero page to authentication, room selection, live gameplay, administration, and final scores. |
- Private drawer knowledge: the selected word is delivered only to the active drawer and never included in shared room snapshots.
- Stable participant identity: drawing authorization and scoring use principal UUIDs rather than display names.
- Authoritative snapshots: newly connected and reconnecting clients receive current membership, phase, drawer, timing, and score state.
- Secure browser sessions: Next.js BFF routes keep backend bearer tokens away from client-side JavaScript.
- Purpose-built WebSocket access: clients connect using short-lived, single-use tickets with explicit origin validation.
- Admin control: administrators can manage word packs, words, and the persistent catalog of bot profiles.
- Bounded room history: chat history is intentionally capped to keep long-lived rooms from growing without limit.
Bots are integrated participants, not decorative chat responses. Administrators create persistent bot profiles; hosts choose from active profiles before the match; and the room can assign a bot as either drawer or guesser.
| As the drawer | As a guesser |
|---|---|
| Receives the private word through its round-scoped runtime | Receives only public masked-word and drawing information |
| Produces validated, normalized drawing strokes | Submits guesses through the same typed command path as humans |
| Must pass the room's stable-ID drawer authorization | Must pass the same round, identity, and scoring checks |
Optional Groq, xAI Grok, and Gemini adapters can power bot drawing and guessing. Without an AI key, Mithril Tiles retains deterministic guessing and template-drawing fallbacks.
Experimental frontier: the bot architecture and complete gameplay path are working, while the quality of AI-generated line art and raw-stroke visual inference remains an active area of exploration. Human multiplayer is the heart of the experience; bots extend it when another challenger is needed.
Browser
|
| HTTPS: pages, authentication, and BFF requests
v
Next.js 16 + React 19
|- themed product interface
|- HttpOnly session-cookie management
|- authenticated API forwarding
`- short-lived WebSocket ticket acquisition
|
| direct WebSocket connection
v
Go API + realtime room server
|- authentication and authorization middleware
|- room actors and authoritative game lifecycle
|- chat, drawing, guessing, scoring, and snapshots
`- round-scoped bot runtimes and provider adapters
|
| pgx
v
PostgreSQL
|- users, guests, tokens, and bot profiles
|- word packs and words
`- games, participants, rounds, and scores
The browser never receives the long-lived backend bearer token. Next.js stores it in an HttpOnly cookie and forwards authenticated HTTP requests from server-side route handlers. Realtime clients request a scoped ticket and then connect directly to the Go WebSocket server.
Detailed engineering references:
| Area | Technology |
|---|---|
| Realtime backend | Go 1.26, net/http, httprouter, coder/websocket |
| Persistence | PostgreSQL, pgx, golang-migrate |
| Frontend | Next.js 16, React 19, TypeScript, Tailwind CSS |
| State and validation | Zustand, TanStack Query, React Hook Form, Zod |
| Drawing | Native Canvas 2D, Pointer Events, normalized strokes |
| Verification | Go testing, Testcontainers, Vitest, React Testing Library |
Prerequisites and complete setup
- Go 1.26.3 or the version declared in go.mod
- Node.js 20.9 or later and npm
- PostgreSQL
- Docker when running Testcontainers-backed API tests
git clone https://github.com/amh1k/mithril-tiles.git
cd mithril-tiles
cd frontend && npm install && cd ..cp .env.example .env
go run ./cmd/apiSet the backend environment at minimum:
DATABASE_URL=postgres://postgres:postgres@localhost:5432/mithril_tiles?sslmode=disable
CORS_TRUSTED_ORIGINS=http://localhost:3000
RATE_LIMIT_TRUSTED_PROXIES=The API listens on http://localhost:4000 by default and applies database migrations during startup.
cp frontend/.env.local.example frontend/.env.local
cd frontend
npm run devThe local frontend environment uses:
BACKEND_API_URL=http://localhost:4000
NEXT_PUBLIC_BACKEND_WS_URL=ws://localhost:4000
APP_ORIGIN=http://localhost:3000Open http://localhost:3000 and answer the Call of Mithril.
Only one provider is selected at startup. Precedence follows the order below.
| Priority | Environment variable | Provider |
|---|---|---|
| 1 | GROQ_API_KEY |
Groq-hosted models |
| 2 | GROK_API_KEY |
xAI Grok models |
| 3 | GEMINI_API_KEY |
Google Gemini models |
| Fallback | No key | Deterministic guesses and template drawings |
GROQ_MODEL can override the configured Groq model. Never commit .env files, provider keys, database credentials, or WebSocket ticket URLs.
# Backend
go test ./...
go test -race ./internal/realtime
go vet ./...
# Frontend
cd frontend
npm test
npm run lint
npx tsc --noEmit
npm run buildDatabase-backed integration tests require Docker. The repository contains focused coverage for data persistence, room lifecycle behavior, provider parsing, realtime events, frontend routes, stores, and room presentation.
Mithril Tiles already delivers its end-to-end multiplayer loop, from identity and room entry through realtime rounds and persisted final scores. Current work is focused on production hardening: richer reconnect recovery, realtime abuse controls, graceful shutdown, observability, CI, browser-level end-to-end coverage, and continued bot-quality improvements.
cmd/api/ HTTP API, middleware, startup, and handlers
cmd/player-test/ development WebSocket client
internal/data/ PostgreSQL models and persistence
internal/realtime/ rooms, game lifecycle, WebSockets, and bots
internal/validator/ backend input validation
migrations/ versioned database migrations
frontend/ Next.js product UI and BFF routes
docs/ architecture and game-flow diagrams
Keep contributions focused, document intentional behavior changes, and add tests when behavior changes. Before opening a pull request, run the relevant quality gates and verify that no credentials, provider keys, ticket URLs, database URLs, or unsanitized logs are included.
May the sharpest eye claim the Mithril.
