# Canvas docs

> Install @texuli/canvas, load a board, edit it, export SVG or PNG. In development.

Language: en
Canonical: https://texuli.com/guides/solutions/canvas/
Markdown: https://texuli.com/guides/solutions/canvas.md (this page). Every texuli.com page has a Markdown twin at its URL plus `.md` (the home page is https://texuli.com/index.md), and any page URL answers `Accept: text/markdown` with its twin instead of HTML.
Last Updated: 2026-11-13

---

Guides

Install @texuli/canvas, load a board, edit it, export SVG or PNG. In development.

[Back to overview](https://texuli.com/solutions/canvas/)

## What Canvas is

Canvas is an embeddable whiteboard: `@texuli/canvas` holds a framework-free model of shapes, text, connectors, groups and frames, typed operations with an op log, CRDT collaboration on `@texuli/document/sync`, a Svelte 5 editor and viewer, and SVG or PNG export with no HTML sinks. The package is published as `0.18.0` and is **in development** (pre-1.0). Pin the exact version or use `^0.17.0`.

It is a board you embed. It does not host canvases, persist them, or serve images. The host app stores the document and resolves image refs.

## What you get

- A validated canvas as data (`format: "texuli-canvas"`, `formatVersion: 1`).
- Shapes, sticky notes, frames, groups, and connectors that route straight, elbow or curved.
- `CanvasEditor` and `CanvasViewer`, with keyboard navigation and a shape tree.
- Local collaboration through the document sync adapter, and `canvasToSvg` / `canvasToPng`.

There is no hosted realtime service. Persistence, transport and image hosting belong to the host.

## Install

Svelte 5 (`^5.57.2`) is a peer. Import the token stylesheet once.

```
npm install @texuli/canvas
```

```
import '@texuli/tokens/base.css';
```

## Load and edit

Canvases from a file, an agent or a share link go through `loadUntrustedCanvas` first. Keep the document in `$state.raw`.

```
<script lang="ts">
  import { CanvasEditor } from '@texuli/canvas/editor';
  import { loadUntrustedCanvas } from '@texuli/canvas/model';
  import '@texuli/tokens/base.css';

  let { json }: { json: string } = $props();
  const loaded = loadUntrustedCanvas(json);
  let canvas = $state.raw(loaded.ok ? loaded.canvas : null);
</script>

{#if canvas}
  <CanvasEditor bind:canvas resolveImage={(ref) => `https://cdn.example.com/${ref}.png`} />
{/if}
```

## Build from operations

```
import { applyCanvasOps, createCanvas, moveOps } from '@texuli/canvas/model';

const canvas = createCanvas({ title: 'Launch plan' });
const added = applyCanvasOps(canvas, [
  { op: 'add', shape: { id: 'a', type: 'sticky', x: 0, y: 0, width: 160, height: 160, text: 'Ship' } },
]);
```

## Format

The canonical document is data: `format`, `formatVersion`, optional `meta` and `grid`, then `shapes` and `connectors`. Array order is z-order. Coordinates are world units. Shape types include rect, ellipse, diamond, line, path, text, sticky, image, frame and group. Connectors route `straight`, `elbow` or `curved`. Styles are a closed token palette; raw CSS colors are refused.

```
import { applyCanvasOps, createCanvas, validateCanvas } from '@texuli/canvas/model';
```

`validateCanvas` and `loadUntrustedCanvas` refuse unknown keys, hostile text, image refs the URL policy rejects, and anything past `CANVAS_LIMITS`. Work is linear in the input. The model has no Svelte and no DOM.

## Operations

Typed operations have inverses. The op log is the single edit path: undo, transforms, grouping and clipboard all go through it. The clipboard is untrusted JSON: size-checked, then validated.

## Editor and viewer

`CanvasEditor` from `@texuli/canvas/editor` is the `role="application"` surface: toolbar, minimap, shape tree, pan, zoom, snapping and transforms. `CanvasViewer` from `@texuli/canvas/viewer` is read-only and exposes the structure as text.

Image refs are resolved by the host. Every URL passes `safeImageSrc` at the untrusted tier by default.

## Export

```
import { canvasToPng, canvasToSvg } from '@texuli/canvas/export';

const svg = canvasToSvg(canvas, { resolveImage: (ref) => `https://cdn.example.com/${ref}.png` });
const png = await canvasToPng(canvas, { scale: 2 });
```

`canvasToSvg` writes escaped markup through `@texuli/core/markup`. `canvasToPng` is browser-only.

## Sync

`@texuli/canvas/sync` binds the canvas to `@texuli/document/sync`. In-memory and `BroadcastChannel` adapters ship; a network transport is the host's. Collaboration is local or in memory, not a hosted service.

## Keyboard and tree

The surface is the only `role="application"` region, named by the canvas title. Shapes take a roving tab stop in reading order. The shape tree (`role="tree"`) lists every shape and connector for screen readers. Changes are announced through the shared live region.

## Motion and contrast

Zoom and fit play through `texuli/motion` and are instant under `prefers-reduced-motion`. Forced colors keep outlines, focus and selection visible. Text paints as SVG text nodes; nothing is parsed as markup.

## Status

Canvas is **in development**. `@texuli/canvas` is on npm as `0.18.0`. Under 0.x a new minor may break. The Kotlin twin validates canvas nodes and does not paint them yet.

## Not built

- A hosted realtime collaboration service (adapters are local or in memory)
- Persistence, network transport and image hosting (the host supplies them)
- Native painting of canvases

## Untrusted canvases

A canvas from an agent, a file or a share link is untrusted data. Use `loadUntrustedCanvas` or `validateCanvas` from `@texuli/canvas/model` before any deep work. Default URL policy is the untrusted tier. There is no HTML sink.

## Catalog and player

Agents emit canvases as data through the `canvas` catalog id (blueprint spec `0.8.0`). `@texuli/catalog` `validateDraft` refuses a `canvas` node unless the host supplies `checkCanvas`. `@texuli/player` paints a valid canvas root through its `canvasHost` seam.

```
import { createCanvasPlayerHost } from '@texuli/canvas/host';

const canvasHost = createCanvasPlayerHost({ resolveImage: (ref) => `/assets/${ref}.png` });
```

Do not invent an MCP operation that is not in `@texuli/tools`. Native painting of agent canvases is planned.
