A persona in the room outputs text. The question is what text it can emit that arrives as a diagram or a drawing rather than prose. This note runs the three candidates you named — Mermaid, Excalidraw, tldraw — as live, working demos on this page, each paired with the exact thing an LLM would have to write to produce it. Then it picks the one that fits the room as it is today, and parks the one that fits where the room is going.
```mermaid fence → the wildcard pane; vendored mermaid + panzoom). See the Scratch-paper interaction demo. The shared canvas (tldraw/Excalidraw) below remains a parked phase-2 idea.Before the demos, the split that matters:
```mermaid fence. A client-side library turns those lines into an SVG. This is identical in shape to how we already render math and code — the model produces text, the renderer draws it. No canvas, no React, no editor.what the LLM emits → a fenced mermaid block
The lightest by a wide margin: one small library, no React, renders to SVG in the browser. A persona writes the diagram the same way it writes a code block. Here is a live render of the room's own turn-routing, drawn entirely from the text below it:
the model output that produced the SVG above — nothing else:
Mermaid also covers sequence diagrams, state machines, class diagrams, ER diagrams, Gantt charts, pie charts and mind-maps from the same fence — the model just changes the first keyword. Because it is pure text, it streams, it diffs, and it survives copy-paste. The cost: it is a fixed grammar — boxes, arrows, lanes. You cannot ask it to "sketch a face" or place a free-form annotation.
Out of the box Mermaid renders the SVG at its intrinsic pixel size and pins an inline max-width on it, so a small diagram sits marooned in a wide frame with ant-sized text. We're phone-first, so the fix has two halves — fluid sizing, and a layout that suits a portrait column:
| Lever | What & why |
|---|---|
| Make the SVG fluid | Strip Mermaid's width/height + inline max-width and let the viewBox drive it (width:100%; height:auto). It then scales up to fill the column and the text grows with it. Mermaid config: flowchart:{ useMaxWidth:true } gives the width:100%; removing the cap lets it grow past intrinsic size. |
| Go vertical (TD/TB) | The biggest mobile lever. A wide LR chart squeezes many nodes across a 375px screen → tiny text. TD stacks them down a narrow column — fewer nodes per row, bigger glyphs. The demo above switched LR → TD for exactly this. |
| Cap the reading column | Full-bleed on a phone; centered and capped (~540px) on desktop, so a simple diagram scales to a comfortable size rather than a giant one. |
| Bump the base font | Set themeVariables.fontSize to 16px and keep node labels short — long labels are what force the auto-shrink in the first place. |
| Escape hatch for dense charts | Some diagrams genuinely can't shrink to a phone. Don't fight it — wrap the SVG in pan/zoom (svg-pan-zoom) or a tap-to-expand lightbox instead of rendering ants. |
the few lines that did it on this page:
mermaid fence in a bubble, hand the body to mermaid.render(), apply the fluid-SVG fix, swap it in. One pinned CDN script, plus one prompt line that tells diagram-inclined hosts the fence exists and to prefer TD over LR — the bubble is a narrow phone column, so vertical is the right default.▶ The scratch-paper interaction — a clickable phone mock: the persona says his line in the bubble and the mermaid lands on the wildcard pane; tap to bring the board up. Live Mermaid inside.
what the LLM emits → an element skeleton (JSON)
Excalidraw is the sketchy, whiteboard-marker aesthetic. The model doesn't write a DSL — it emits a skeleton array of elements (rectangles, ellipses, arrows, text, with ids so arrows can bind), which convertToExcalidrawElements() expands into a full scene the editor mounts. It is a real editor: pan, zoom, grab a shape and move it.
the skeleton an LLM would emit (rendered above once activated):
Strengths: genuinely free-form (arbitrary positions, the hand-drawn look, embedded text), an editable result a human can keep working on, and a clean export to PNG/SVG. Costs: it is a React app you mount — not a string in a bubble — and the model has to emit valid, positioned JSON, which is wordier and more error-prone than a DSL.
what the LLM emits → calls to editor.createShapes([...])
tldraw is the most capable canvas of the three and the only one built around real-time multiplayer. You don't hand it a static scene; you mount the editor and, on its onMount, drive it through an API — createShapes(), createBindings(), zoomToFit(). The "LLM output" is therefore a sequence of shape records the host code passes to that API.
the shape records an LLM would produce, fed to the editor on mount:
Strengths: the richest shapes, arrow bindings, frames, a polished editor, and a sync store for collaborative multi-cursor canvases — which maps directly onto our multi-user, SSE model. Costs: the heaviest bundle, the steepest API, and licensing to read before any production use (tldraw's watermark/license terms).
| Library | What the model emits | Renders via | Weight | Streams? | Free-form? |
|---|---|---|---|---|---|
| Mermaid | a mermaid text block (DSL) | client → SVG, no React | tiny | ✓ it's text | ✗ fixed grammar |
| Excalidraw | an element skeleton (JSON) | mounted React editor | heavy | ✗ needs whole scene | ✓ hand-drawn |
| tldraw | createShapes() records | mounted React editor | heaviest | ✗ API-driven | ✓ + multiplayer |
```mermaid fence lifts out of the speech and renders on the wildcard pane (vendored mermaid + panzoom). It was a small PR, not a new surface.In one line: Mermaid is a renderer we add to the pipeline; a whiteboard is a surface we'd build. Start with the renderer.
Mermaid — docs & live editor · Mermaid render API
Excalidraw — browser integration (ESM CDN) · Element skeleton API
tldraw — quick start · Controlling the canvas (createShapes) · tldraw license