← Projects · 2026-07-15

scrawl

A shared ASCII doodle wall you connect to over SSH. Everyone connected right now is drawing on the same canvas, live.

Live sitescraw.games.goonk.se → Repoexplewd/scrawl →

Highlights

  • No shell, no exec — wish hands each SSH connection straight to a bubbletea program, structurally incapable of reaching a real shell on the host, same model as delve-term
  • Live multiplayer state: a small in-memory registry of every connected session's *tea.Program broadcasts a redraw the instant anyone paints
  • Found and fixed a real deadlock — Program.Send() blocks until Run() is actively reading, so broadcasting synchronously from Join()/Paint() froze every session before it ever rendered; fixed with go p.Send(...)
  • Found and fixed a subtler bug — lipgloss's default styles detect color support from the server process's own stdout, not any session's actual terminal, so a redirected log file silently killed all color/background styling server-wide; fixed with a per-session lipgloss.Renderer forced to TrueColor
  • Verified with two scripted concurrent SSH sessions, not just live poking — one paints, the other sees the broadcast, peer count stays in sync

A shared ASCII doodle wall reachable over raw SSH — connect and you’re painting on the same canvas as everyone else connected right now, live.

ssh scraw.games.goonk.se -p 23235

Not aiming to be useful to anyone but me. Built purely because a multiplayer canvas over SSH is a fun, weird shape for a toy, and the wish+bubbletea pattern from delve-term was still fresh enough to reuse without re-learning it. The one thing this app needed beyond that pattern: multiple sessions sharing live state, which meant building the tea.Program directly instead of hiding behind wish’s usual middleware helper, so a small registry could hold a reference to every connected session and broadcast a redraw the instant one of them paints.

Two real, subtle bugs came out of that: a synchronous broadcast that deadlocked the very first connection before it ever rendered anything, and a color-detection quirk in lipgloss that silently stripped all color server-wide because it was reading the server process’s own (redirected) stdout instead of each session’s actual terminal. Both fixed, both covered by regression tests.