livefigures
v0.8.0
Published
Editable, version-controlled figures: render/validate Excalidraw, Vega-Lite, Graphviz, PlantUML and 13 more formats — CLI and MCP server for the quarto-livefigures extension
Maintainers
Readme
quarto-livefigures
Editable, version-controlled figures as first-class Quarto citizens.
Reference the figure's source file with normal figure syntax and
quarto render does the rest — no manual SVG/PNG exports, no generated
files in version control.
{#fig-arch width=80%}
{#fig-totals}Supported source formats:
| Format | Extension | Best for |
| ------ | --------- | -------- |
| Excalidraw | .excalidraw | hand-drawn diagrams and sketches |
| Vega-Lite | .vl.json | data-driven charts (and LLM/agent-authored figures) |
| Vega | .vg.json | low-level chart specs, force-directed graphs |
| nomnoml | .noml, .nomnoml | node-edge/UML diagrams from terse text |
| WaveDrom | .wavedrom, .wavedrom.json | digital timing & register diagrams |
| bytefield | .bytefield | byte/packet layout diagrams |
| Graphviz | .dot, .gv | classic graph layouts (file-referenced; complements Quarto's code-cell dot) |
| DBML | .dbml | database schema diagrams |
| PlantUML † | .puml, .plantuml | UML: sequence, class, activity … |
| D2 † | .d2 | modern declarative diagrams |
| C4-PlantUML † | .c4 | C4 architecture diagrams |
| Structurizr † | .structurizr | C4 via the Structurizr DSL |
| erd † | .erd | entity-relationship diagrams |
| ditaa † | .ditaa | ASCII art → polished diagrams |
| pikchr † | .pikchr | PIC-style technical diagrams |
| svgbob † | .svgbob | ASCII art → SVG |
| TikZ † | .tikz | LaTeX diagrams (complete standalone docs) |
† Rendered via a kroki endpoint — the one backend
class that needs the network (on cache misses only). The diagram source is
sent to the endpoint; for private diagrams, self-host kroki and set
livefigures: kroki-url: <url> in your metadata. All other formats render
fully offline.

The source file is the single source of truth. Captions, labels, cross-references, sizing, layout, subfigures, and lightbox all work exactly as for any other Quarto figure.
Installation
quarto add seandavi/quarto-livefiguresRequires Node.js >= 18 on your PATH (the only external dependency). Rendering is fully offline — fonts and the rasterizer ship with the extension.
Enable the filter in _quarto.yml (or document front matter):
filters:
- at: pre-ast
path: livefigures(The at: pre-ast placement makes cross-references work on inline
code-block figures; a plain filters: [livefigures] also works if you
only use file-referenced figures.)
Usage
Any Quarto image whose target is a supported source file is rendered at
build time into a content-addressed cache (_livefigures/, add it to
.gitignore) and flows through Quarto's native figure pipeline:
{#fig-flow width=60%}
See @fig-flow for details.Small diagrams can live inline as fenced code blocks instead of files — same pipeline, cache, and figure semantics:
```{.nomnoml #fig-pipe fig-cap="The pipeline"}
[Filter] -> [Cache] -> [SVG]
```Block classes: .excalidraw, .vega-lite, .vega, .nomnoml,
.wavedrom, .bytefield, .dot, .dbml, .plantuml, .d2, .c4,
.structurizr, .erd, .ditaa, .pikchr, .svgbob, .tikz.
- HTML formats (articles, websites, books, dashboards, RevealJS): SVG
with the hand-drawn fonts embedded — correct offline and in
embed-resources: truedocuments. - PDF/LaTeX: high-resolution PNG rasterized by the bundled renderer, so fonts are always correct (LaTeX's SVG conversion is not required or used).
Re-renders happen only when the scene, options, or extension version change; otherwise the cache is reused.
Options
Per figure (attributes) or project-wide (metadata):
| Option | Values | Default |
| ------------ | ----------------------------------- | --------------------------- |
| theme | light, dark, auto | auto (HTML), else light |
| background | transparent, scene | transparent |
{theme=dark background=scene}livefigures:
theme: light
background: scenetheme: auto renders once (light); Excalidraw figures restyle on dark
pages with the same CSS filter Excalidraw itself uses for dark mode.
Charts (Vega/Vega-Lite) deliberately stay light under auto — inverting
data-encoded colors would misrepresent them; use an explicit theme=dark
for the vega dark theme. For Excalidraw, theme=dark performs a true dark
export.
For AI agents
skills/livefigures/SKILL.md is a single-file briefing that teaches a
coding agent this extension: syntax (file vs fenced block), a
format-selection table with per-format doc links, options, and failure
modes. Drop it into your project (e.g. .claude/skills/livefigures/) or
paste it into any system prompt. See
livefigures.seandavis.net/agents
for the workflows it enables.
MCP server — agents can see their figures
An MCP server exposes the same renderers as tools (render, validate,
list_formats): an agent writes figure source, calls render, and gets
the figure back as an image — so it can visually check and fix its own
work before the figure lands in a document. Rendering matches
quarto render (same engines, fonts, options).
Public server (nothing to install):
claude mcp add --transport http livefigures https://mcp.livefigures.seandavis.net/mcpLocal (ships with the extension, offline for local formats):
claude mcp add livefigures -- node _extensions/seandavi/livefigures/mcp.mjsAny MCP client works; the server also serves the skill as the
livefigures://skill resource. On the public server, graphviz and dbml
render via kroki (Workers can't run their wasm); everything else renders
with the extension's own bundled engines. See ADR 0015.
CLI
The same tools as commands — the CLI ships inside the extension
(_extensions/seandavi/livefigures/cli.mjs, no install beyond quarto add):
node _extensions/seandavi/livefigures/cli.mjs render figures/arch.excalidraw -o arch.png
node _extensions/seandavi/livefigures/cli.mjs validate figures/*.dot # exit 1 on errors
node _extensions/seandavi/livefigures/cli.mjs formats # what can I write?
node _extensions/seandavi/livefigures/cli.mjs mcp # = mcp.mjs
echo 'digraph { a -> b }' | node _extensions/seandavi/livefigures/cli.mjs render - --format graphvizExamples
See examples/ for a minimal article,
book, and RevealJS deck.
Limitations
- Windows is untested (macOS and Linux are exercised; Windows CI is a planned fast-follow).
- DOCX and EPUB are untested ("may work"); verified formats are the HTML family and PDF.
- CJK text (Excalidraw's Xiaolai font, 13 MB) is not bundled; scenes using it fail with a clear error. Open an issue if you need it.
- Errors are deliberate and loud: a missing Node runtime or a corrupt source file aborts the render rather than publishing a broken figure.
How it works
A Lua filter rewrites .excalidraw image targets to cached assets produced
by a bundled, headless Node renderer (Excalidraw's own export code + a WASM
rasterizer). Design decisions are recorded in
docs/ARCHITECTURE.md and
docs/adr/.
The name is deliberate: Excalidraw is the first backend, not the last — the roadmap includes other editable-figure formats (see issue #7).
Contributing, license, citation
Contributions welcome — see CONTRIBUTING.md and the Contributor Covenant. MIT licensed. If you use livefigures in academic work, CITATION.cff has a citable reference (GitHub's "Cite this repository" button uses it).
Development
cd renderer && npm install && npm run build # rebuild the committed bundle
node --test tests/test.mjs # end-to-end tests (needs quarto)