collab-board

Full-Stack
2026-05-11T10:54:48Z
GoSvelteTypeScriptViteFullstack

collab-board is a single-binary fullstack app: a Svelte 5 (runes mode) + TypeScript SPA built by Vite, embedded into a Go server via //go:embed. One binary, zero runtime dependencies on top of the OS, and one deploy. The frontend bundle is ~9 KB JS / ~4 KB CSS gzipped. No UI library, no router, no state framework, just a small reactive client class and a canvas controller that owns the drawing surface.

The UI is pointer-event driven so touch and stylus work alongside mouse without a second code path. The eraser uses canvas destination-out compositing to remove pixels rather than paint over them, and the canvas controller keeps a replay buffer so resizing the window (or arriving late to a room) both rebuild the raster from the same list. New members get a server-sent snapshot of the room's strokes before any new broadcasts, so they walk into a board that already has art on the walls instead of an empty canvas. The visual identity leans into a warm, atelier-toned dark theme with an amber accent rather than the default cool-blue SaaS aesthetic.

Under the hood the concurrency model is unchanged from the all-backend version and is still the project's most interesting design choice. Each connection runs two goroutines (one read pump, one write pump) communicating over a bounded outbound channel. Each room runs one goroutine that owns the member map and the bounded stroke history (no mutexes, no shared state) and processes join/leave/inbound events serially. Broadcasts use a non-blocking TrySend: if a client's send buffer is full, the room evicts that client rather than stall the whole room on the slowest member. The bundled load test demonstrates this: it can deliberately freeze one client and show the server evicting it while the other 49 stay healthy.

The runtime stack stays deliberately small. Server: Go standard library (net/http, log/slog, embed, context, sync/atomic) plus one third-party dependency (coder/websocket). Client: Svelte + Vite at build time only. Observability is a hand-rolled Prometheus /metrics endpoint and JSON-structured slog output. The Dockerfile is a three-stage build (node for the frontend, golang for the backend, distroless/static-debian12:nonroot for the runtime) for a minimal attack surface and a small image.

  • Svelte 5 (runes) + TypeScript SPA, built by Vite, embedded into the Go binary via //go:embed, single-binary deploy preserved
  • Pointer-event drawing surface: touch, stylus and mouse share one code path
  • Pen and eraser tools (eraser via canvas destination-out compositing); 8-color palette and three stroke widths
  • Server-sent stroke snapshots for late joiners, with a bounded ring buffer (cap 2000) on the room goroutine
  • Live, name-labeled remote cursors with deterministic per-user color and idle fade-out
  • Shareable room URL with one-click copy; randomly generated codes; URL ↔ room sync
  • Floating glass UI (room badge, presence stack, tool dock, chat drawer, theme toggle) over a full-bleed canvas
  • Warm-toned dark theme (amber accent) with light-mode toggle, persisted across sessions
  • Multi-room WebSocket server with goroutine-per-room ownership of state and stroke history
  • Non-blocking broadcast fan-out with bounded send buffers (cap=64) per connection. Slow clients are evicted, never stalling the room
  • Read/write pump separation with context-driven cancellation; ping/pong from inside the write pump (single-writer invariant)
  • Hand-rolled Prometheus /metrics endpoint (rooms_active, members_active gauges)
  • Bundled load test with deliberate slow-client mode to verify backpressure design
  • Three-stage Dockerfile (node → golang → distroless:nonroot) for a small, hardened image
  • ~9 KB JS / ~4 KB CSS gzipped on the client; one third-party runtime dep (coder/websocket) on the server