New
Apps 1,503Makers 1,501This week 1,503
Promote your app−30%
cacographer screenshot 1
0views in
the last 30 days

cacographer /kəˈkɒɡ.rə.fər/ (noun)

1. someone who is bad at spelling or handwriting.

2. someone who can draw funny pictures.


FOSSA Status FOSSA Status

If Core built skribbl.

A real-time multiplayer drawing-and-guessing game for a trusted group of coworkers. One player at a time is the artist, the rest are simple cacographers — racing to guess the word in chat. No accounts, no database — share a link, play, done.

Stack: This app uses a trimmed down version of DaveStack, which is a template for building SvelteKit apps with Bun. No datbase here, we're just using in-memory game state with Bun-native WebSockets.

To read more on the product and technical scope of this project, please read:

Getting started

Requires Bun 1.4 or newer (no other runtime is used, on either client or server). The floor is declared in package.json's engines.bun; Bun itself doesn't enforce that field, so check bun --version and bun upgrade if you're behind — CI and the Dockerfile pin to it.

bun install
bun run dev        # game server (:3001) + Vite dev server (:5173), together

Open http://localhost:5173, create a game, open the room link in a second tab to play against yourself.

Project layout

server/
  index.ts          # Bun entry point: HTTP + WebSocket wiring (the only transport-aware file)
  engine/            # transport-agnostic game logic — rooms, scoring, masking, words
    Room.ts          # per-room state machine; takes intents, emits messages via injected deps
    RoomManager.ts   # creates/looks up rooms
    *.test.ts        # bun:test coverage, one file per engine module
  data/words.json    # word list

src/
  lib/
    protocol.ts      # single source of truth for every message shape on the wire
    game.svelte.ts    # client-side game store (Svelte 5 runes)
    realtime/         # WebSocket client + URL helpers
    components/       # Canvas, Chat, PlayerList, Timer, WordBlanks, SettingsPanel
  routes/             # SvelteKit pages (lobby, game/[code])

docs/
  PRD.md              # product scope — read before changing behavior
  TRD.md              # architecture/design rationale — read before changing structure

Contributing

Before making non-trivial changes, read CLAUDE.md — it has the load-bearing architecture rules (server is the sole authority, protocol.ts is shared source of truth, the engine stays transport-agnostic and testable) plus style and workflow conventions. The short version:

  • Small, logical commits on a feature branch; PR into main; CI must be green before merge.
  • Game logic lives in server/engine/ only — never in the client, even for perceived responsiveness.
  • Changing the wire protocol? Update src/lib/protocol.ts and both sides of the wire in the same commit.
  • New engine behavior needs a bun:test in server/engine/*.test.ts; anything timing-dependent must go through injected deps.now/deps.schedule, not raw setTimeout/Date.now().

Run the full CI suite locally before pushing:

bun run lint          # oxlint, strict — must be clean
bun run format        # oxfmt --write (tabs, single quotes, 100 cols)
bun run format:check  # what CI runs
bun run check         # svelte-check --tsgo-experimental-api via scripts/check.ts (client, src/**), fails on warnings
bun run check:server  # tsc (server/**, via server/tsconfig.json)
bun test server       # engine unit tests
bun run build         # static client → build/

Production

bun run build      # static client → build/
bun run start      # one Bun process: HTTP (static client + API) + WebSockets on $PORT

Railway

Single service, no database:

  • Build command: bun install && bun run build
  • Start command: bun run start
  • Railway injects PORT; the server binds to it. Game state is in-memory — a redeploy drops live games, which is fine for v1.

Architecture in one paragraph

One Bun process holds all game state in memory (RoomManager → Room), coordinates each room over WebSockets, and serves the static SvelteKit client. Clients are thin views: they render server-pushed state and send intents; the server is the sole authority on words, timing, guess correctness, and scoring. The engine (server/engine/) is transport-agnostic and fully unit-tested; src/lib/protocol.ts is the single shared source of truth for message shapes on both sides of the wire.

Description taken from the repository README.

Ratings

–
0 ratings
5 0
4 0
3 0
2 0
1 0

Rate this app

Sign in to rate

Comments 0

Sign in to comment

No comments yet.

Related apps

#drawing · #multiplayer · #party-game