Canvas language
The visual grammar of a Visualli map. Every rule carries one tag:
- SPEC — what a
.visuallifile holds and how any viewer renders and browses it, read-only. Normative and exact: a conforming viewer looks and moves like Visualli, pixel for pixel and millisecond for millisecond. The open React SDK implements SPEC, the desktop viewer is SPEC and nothing else, and the Visualli apps import that same SDK — the experience outside developers get is the one we use ourselves. Candidates for the open standard at spec.visualli.ai. - PRODUCT — what the Visualli apps add on top: AI (Go deeper, Map it, live generation), editing and moving ideas, sources, chat, vault, export and app chrome. Product data may travel in a file as extensions; SPEC viewers ignore extensions they don't know and never drop them.
The .visualli terms are used as-is: layer, level, node, connection, container, extension.
The split at a glance
| SPEC — file + read-only rendering (open SDK) | PRODUCT — the Visualli apps |
|---|---|
Layers, levels, layouts; nodes with label, summary, color, position; connections; containers — including everything the AI added with Map it, once it's in the file |
Live generation: streaming a file while the AI writes it, ThinkingTree |
| The look: six blob shapes, one per level; depth rings; sizes; labels and fonts; connectors and arrowheads; containers; chromatic immersion | Go deeper (the button, the facets panel and the facets extension), Visualize it / Map it as actions |
| The 8 themes (Light, Dark, Focus, Color-safe, High contrast) with their exact values, plus reduced motion, readable type and larger text | Sources: the Sources panel, source chips and the sources extension |
| Motion: bloom, connector draw, dive in, back out, hover and select, reduced motion | Edit mode, the edit operations (applyEdit), undo |
Read-only browsing: peek, term cards (semantic-anchors), step inside / back out, depth trail, zoom / pan / fit, touch rules, keyboard |
Moving ideas and Reset layout (writing position) |
Components: VisualMap (read-only), Node, Edge, DepthTrail, CanvasControls, SemanticPeek, SemanticAnchor / AnchorCard, blobPath / drawBlob / RINGS / edgePath, theme tokens |
Components: GoDeeper, SourcesPanel, EditBanner / SelectionBar / InlineText / SummaryEditor, Composer, ChatTurn, Vault, ComfortPanel, ThinkingTree / TreeMark, ExtensionPanel, export |
Principles
- Visuals first, text last. A node is a short label (≤ 6 words). Detail lives in a deeper layer or a peek — never on the canvas. SPEC
- Depth is spatial. You go into an idea, not to another page. SPEC
- Reveal gradually. A layer arrives one idea at a time, center first. SPEC
- Only connect what is insightful. A connector exists only when the relationship itself teaches something, and it always has a label. Hierarchy alone (parent → child) is shown by layers, not by lines. SPEC
Node anatomy — SPEC
| Part | Rule |
|---|---|
| Outline | One of six hand-authored organic blobs (BLOB_SHAPES, drawn with midpoint-quadratic smoothing). All nodes in a layer share one shape; consecutive levels never repeat a shape (shapeForLevel). |
| Size | node-width 200 × node-height 148 at zoom 1; the level-0 center node is 240 wide (size="root"). |
| Fill | node.data.color — a topic name (teal … stone) or any CSS color. Absent: topics are assigned round-robin by sibling order. |
| Outline stroke | topic-*-ring, 3px (node-stroke), 4px when selected. |
| Label | node-label (Kalam 700, 22px) in node-ink, centered, ≤ 3 lines then ellipsis. The center node uses node-root. Labels scale inversely with zoom up to 1.3× so they stay legible when zoomed out; hidden below zoom-min. |
| Depth rings | Rings say "there is more inside". Count = number of levels beneath the node, capped at 3. Ring 1 at 1.10× (2px, 50%, −5°), ring 2 at 1.20× (1px, 40%, −10°), ring 3 at 1.30× (3px dashed 6 4, 30%, −15°). A node without rings is a leaf and is not clickable. |
Topic colors and themes — SPEC
Eight pastels derived from the logo: topic-teal, -harbor, -iris, -berry, -coral, -amber, -sun, -stone. Siblings get different topics; a child layer may reuse its parent's topic for the idea that continues it. Colors do not encode meaning — no legend, no "red = risk". Meaning belongs in labels, rings and connector style.
- The 8 themes are normative, with the exact values in
tokens/tokens.json: Light and Dark, Focus (desaturated, for ADHD minds), Color-safe (Okabe & Ito-derived) and High contrast, each light and dark. Every viewer offers them. SPEC - Comfort settings that change rendering are normative too: reduced motion, readable type (hand faces fall back to the UI face) and larger text. SPEC · the panel that sets them (
ComfortPanel) is PRODUCT. - A file's
themesextension may add themes ({ "id":"themes", "data":[{ "name":"…", "topics":{ "teal":"#…" } }] }); it never replaces the eight. SPEC
Connectors — SPEC
- A cubic curve that bows perpendicular to the chord — at most
edge-arc-max(45px), flatter as distance grows (edgePath). Endsedge-gap(15px) outside the outermost ring. Open V arrowhead (11px arms at ~33°). Strokeedge,edge-width2.5px, round caps. style: "solid"= a direct or causal relationship;style: "dashed"(7 7) = an associative one.- The label rides the curve in
edge-label(Caveat 500, 18px, lowercase, ≤ 4 words), set above the line with acanvashalo, always reading left-to-right. - Connections stay within one layer.
Containers — SPEC
A dashed rounded hull (line-strong, 10 6, radius 48) with its label in note 22px above the top-left corner. style: "none" draws nothing. Containers group; they never create depth.
Layers, levels and layouts
- Level 0 is the overview; every child layer is attached to one node (
parentNodeId). Sibling layers on one level show one at a time. SPEC layout: radial— center idea plus a ring of peers (the default).linear-horizontal— sequences and timelines, gently staggered up/down.linear-vertical— ranked steps. Layouts seed positions only for nodes that have none. SPECpositionis authoritative once set: viewers render each node where the file puts it. SPEC- Moving ideas (drag with mouse or finger, Option/Alt + arrows) writes the new
positionback to the file, so the arrangement is the person's own; Reset layout restores the generated one. A press that moves more than 4px (mouse) or 10px (finger) drags; a still press is a tap; the click that ends a drag is ignored. PRODUCT - Chromatic immersion: inside a layer, the canvas takes the parent node's topic fill at
immersion-alpha(15%), so you always know whose inside you're in. SPEC
Navigation — SPEC
- Step inside: enter an idea's inner layer — click a ringed node (mouse), tap a selected ringed node a second time (touch), press Enter on it, or use Step inside on its peek. The camera dives into the node (scale ≈ 3.2 around it,
duration-zoom,ease-zoom), fades at 150ms, swaps at ~320ms, and the new layer blooms. - Back out: the depth trail, Escape or Backspace, or pinch out past
zoom0.4. The layer shrinks to 0.6 and fades (220ms), the parent settles in from 0.94. - Depth trail (
DepthTrail): a vertical stack top-left — one organic dot per level, in that level's shape and topic, joined by a dashed stem; the current level is a raised pill. The first item carries a home glyph and the Visualli's title. - Zoom, pan, fit (
CanvasControls): zoom −, %, +, and Fit map to view; ⌘+ / ⌘− / ⌘0; pinch and two-finger pan on touch. - The interaction mode follows the pointer in use, not the screen size: a touchscreen laptop switches the moment someone taps, an iPad with a trackpad hovers. Double-tap is left to the system's zoom.
- On touch, a one-time Caveat note teaches the canvas: "tap an idea to peek · tap again to step inside".
Understanding: peek, term, go deeper
Every idea can be understood without leaving the map. Step inside moves you through the map; Go deeper explains one thing in depth. They never share a name.
| Depth | Component | What it shows | Mouse / keyboard | Touch | Tag |
|---|---|---|---|---|---|
| 1 · Peek | SemanticPeek |
The idea's summary (1–2 sentences) with its terms, and Step inside if it has rings |
Hover or focus the idea → card beside it, never over it | First tap → bottom sheet; the idea lifts, others dim, the canvas nudges it above the sheet | SPEC |
| 2 · Term | SemanticAnchor / AnchorCard |
One underlined term's definition and Learn more ↗ | Hover the term 250ms (stays while hovered, closes 300ms after leaving), or Tab + Enter → card layered over the peek; Escape returns focus | Tap the term → the sheet swaps to its definition, ‹ back to the idea | SPEC |
| 3 · Go deeper | GoDeeper |
Five facets — What it means · How it works · What it's like · A worked example · What it's not — streamed in, then Visualize it | Docked panel on the right (380px); the map makes room, the source idea stays highlighted | Sheet up to just below the top bar, ‹ back to the peek | PRODUCT |
- Terms come from the
semantic-anchorsextension. They are underlined dotted inlink(1.5px, offset 3px) wherever they appear in a summary — real buttons, so keyboards and screen readers reach them. A term's own card never underlines further terms. SPEC - The peek's Go deeper button, its source line and the term card's Go deeper are added by the product (
VisualMapdeeper,source); a SPEC viewer's peek has summary, terms and Step inside only. PRODUCT - Go deeper works for terms and for whole ideas. From a peek it explains the idea; from a term card it explains the term, with "in ‹idea›" as context. Facets are AI-written text, streamed one by one: queued, writing (spinner), ready (opens itself if nothing else is open), or Retry. Icons are Lucide — lightbulb, cog, shapes, flask, ban — never emoji. A polite live region counts "3 of 5 ready". Saved facets may travel in a
facetsextension, which SPEC viewers ignore. PRODUCT - Visualize it closes the panel: "‹subject› could use a map of its own." The button Map ‹subject› asks where — Inside this map (a new layer under the idea; a ring appears and you can step inside) or As a new Visualli (its own map in the vault). PRODUCT
- What Map it creates is SPEC content. The AI's expansion is written as ordinary
.visuallidata — a child layer under the idea (or a new file) with nodes,summarytexts, connections andsemantic-anchorsterms. Once saved or exported, every SPEC viewer renders and browses it like any other layer: the new ring, step inside, peeks, terms. Only the act of generating (the button, the panel, streaming) is product. SPEC (the result) · PRODUCT (generating it) - Layouts by surface for Go deeper and the Sources panel (
VisualMappicks automatically): web app → panel; phones, tablets and narrow canvases → sheet; short embeds under 460px tall → stack (replaces the map view, ‹ back returns). The MCP card turns Go deeper off (deeper={false}) and hands it to Open in Visualli. PRODUCT - Sources — what a Visualli is made from, passages linked to ideas — live in the product's
SourcesPaneland itssourcesextension; SPEC viewers ignore them. PRODUCT
Editing — PRODUCT
Reading is the default; changing a Visualli is a deliberate mode, and only the Visualli apps have it. Edit (pencil + word) in the top toolbar turns it on; Done in the editing bar turns it off. While editing, everything about browsing still works — the trail, stepping inside (from the selection bar), peeks on hover — so people edit any layer as they move through it. A quiet dot grid on the canvas says "you can change this".
One grammar for every construct
- Select — click or tap any construct: an idea, a connector, a group (and whatever comes next). It takes a
focusoutline and its selection bar appears beside it. - Verbs — the selection bar lists that construct's verbs in a fixed order: text (Rename, Summary, Edit label) → structure (Connect, Group, Edit ideas) → navigate (Step inside) → delete (Delete, Ungroup). The order never changes, so a new construct is learned at a glance.
- Add — the editing bar's Idea and Group (future constructs join this list); double-click empty canvas adds an idea where you clicked.
- Choose… — multi-step verbs (Connect: choose the idea to connect to; Group: choose ideas) put their instruction in the editing bar with Cancel (and Done when needed). The same on mouse, touch and keyboard.
- Every change is one op (
node.rename,connection.add,container.delete, …) — persisted by the app, undoable with Undo/Redo in the editing bar or ⌘Z / ⇧⌘Z.
| Construct | Select | Text | Structure | Delete |
|---|---|---|---|---|
| Idea | click / tap | Rename (or click it again, double-click, F2) · Summary (in its peek; names any underlined terms the change would drop) | Connect · Group · Step inside | Delete — ideas with inner layers confirm first ("Its 2 inner layers and 6 ideas will be deleted too. You can undo this.") |
| Connector | click / tap the line | Edit label (required — a new connector asks "how are they related?") | — | Delete |
| Group | click its outline or name | Rename | Edit ideas (choose…) | Ungroup (keeps the ideas) |
- Narrow maps (phones, small embeds, under 560px): the editing bar moves to the bottom and its tools go icon-only (names stay for screen readers); the selection bar goes icon-only too. Hide the Composer while editing on phones so the bar has the bottom edge.
- Moving ideas is layout, not editing: it works in both modes and is saved per person. The first move in view mode invites "Layout saved. Want to edit the content too? Edit" once; apps may opt into switching modes automatically instead.
- Keyboard: Tab to an idea, Enter selects (Enter again renames), F2 renames, Delete removes, Escape cancels a step, then deselects. Inline editors: Enter saves, Escape cancels; summaries save with ⌘ Enter.
- No AI in edit mode — AI changes go through the chat and arrive as receipts with Undo.
Choreography
| Moment | Motion | Tag |
|---|---|---|
| Layer appears | Nodes bloom from 50% of the way to the center: scale .55 → 1, duration-reveal 420ms, ease-bloom, staggered stagger-reveal 70ms in sibling order. Then solid connectors draw from source to target (500ms), dashed ones fade in. |
SPEC |
| Hover / select | Node lifts 2px; rings turn −2° and grow 1.5% (no overshoot — a nudge, not a wobble); Focus palette dims every other node to 40%. On touch the selected node also takes a 4px outline and others dim to 60%. | SPEC |
| Sheet (touch peek) | Slides up from the bottom edge (duration-base, ease-standard), follows the finger while dragged, snaps back unless pulled 80px. The canvas pans the selected idea into view with the same timing. |
SPEC |
| Reduced motion / Gradual reveal off | Everything appears together with a 120ms fade; dives become cross-fades; sheets fade in 120ms. | SPEC |
| Live generation (the AI is still writing the file) | Nodes bloom as their lines arrive; a ThinkingTree sits in the chat dock. Never a spinner over the canvas. |
PRODUCT |
| Drag | The held idea lifts (scale 1.04, deeper shadow or dark-mode glow) and follows the pointer with no easing; connectors redraw every frame; on drop it settles at once. No snapping grid — placement is personal. | PRODUCT |
Canvas 2D / SDK notes — SPEC
Use blobPath (SVG) or drawBlob(ctx, shape, rx, ry, cx, cy) (Canvas 2D) — same points, same smoothing. RINGS holds the ring recipe, edgePath / arrowPath the connector geometry. Read colors from the CSS custom properties at draw time (getComputedStyle(document.documentElement).getPropertyValue('--topic-teal')) and redraw on a theme change so all eight themes stay correct. These are exported from @visualli/design-system/spec — the layer the open SDK and the Visualli apps share.
