Shao-En Ju139 downloadsExport a Canvas to a single self-contained HTML file you can share.
Export an Obsidian Canvas to one self-contained HTML file you can email, drop in a folder, or open on any machine. The exported file zooms and pans the way the canvas does inside Obsidian.
No server. No build step for the reader. No network requests at all — every image is inlined, every font reference is stripped, and nothing phones home when the file is opened.
main.js, manifest.json, and styles.css from the latest release.<your vault>/.obsidian/plugins/canvas-to-html/.If your vault syncs through iCloud, copy the files in. Do not symlink them — iCloud does not sync symlinks reliably.
Add sam080626-arch/obsidian-canvas-to-html as a beta plugin in
BRAT.
Open a canvas and run Export canvas to HTML from the command palette, or
right-click a .canvas file in the file explorer. The .html lands next to the
canvas unless you set an output folder in settings.
| Input | Action |
|---|---|
| Two-finger scroll / wheel | Pan |
| Ctrl/⌘ + wheel, trackpad pinch | Zoom at the pointer |
| Drag empty space | Pan |
+ / - |
Zoom in / out |
0, double-click empty space |
Fit to screen |
| Click an edge | Frame both of its endpoints |
Cards taller than their frame scroll internally. Corner buttons zoom, fit, and toggle light/dark; the theme choice persists.
| Canvas node | Becomes |
|---|---|
| Text card | Rendered Markdown |
Embedded .md note |
The note's rendered content, one level deep |
| Image | An <img> with an inlined data URI, downscaled past a size limit |
| A labelled placeholder card — the bytes are never inlined | |
| Web link | A clickable link card, not a live iframe |
| Group | A tinted rect with its label, behind the cards |
| Edge | An SVG Bézier with arrowheads, in canvas coordinates |
Callouts, tables, task lists, code blocks with syntax colouring, math, and images embedded inside notes all survive. The stylesheet mirrors Obsidian's own default theme values — the type scale, spacing, and the canvas colour palette — so an export reads like the canvas it came from.
| Setting | Default |
|---|---|
| Output folder | Next to the source canvas |
| Maximum image dimension | 2000 px |
| Image re-encode quality | 0.85 |
| Size warning threshold | 25 MB |
| Default theme in the export | Follow the reader's system |
| Open the file after exporting | Off |
![[embed]] inside an embedded note
is not expanded.npm install
npm run dev # watch build
npm test # vitest
npm run lint # the same rules Obsidian's plugin validator applies
npm run build # lint + typecheck + production bundle
Two bundles are produced: the plugin (main.js) and the viewer runtime, which is
inlined into the plugin as a string at build time and written into every export.
Logic lives in pure modules; the Obsidian-coupled modules are thin and take their dependencies through injected interfaces, so the whole pipeline is testable without the app.
| Module | Responsibility | Obsidian API |
|---|---|---|
canvas-model.ts |
Parse .canvas JSON into a Scene, compute world bounds |
no |
edges.ts |
Bézier routing and arrowhead geometry | no |
assets.ts |
Image sizing, encoding, data URIs, caching | no |
math.ts |
TeX source extraction, MathJax CSS sanitation | no |
clean-rendered.ts |
Strip app-only chrome, scrub links, carry math across sanitization | no |
serialize.ts |
Assemble the exported document | no |
viewer/transform.ts |
Zoom/pan/fit maths | no |
viewer/viewer.ts |
The runtime shipped inside each export | no |
resolve.ts |
Canvas nodes → renderable HTML | injected |
render-markdown.ts |
MarkdownRenderer + sanitization + math |
yes |
main.ts |
Commands, menus, settings, orchestration | yes |
159 tests across unit, golden-file, and jsdom suites:
npm test
The jsdom suites drive the exported viewer with real wheel, pointer, and
keyboard events and assert the resulting transform, because that layer cannot be
covered by unit tests alone. MANUAL-VERIFICATION.md lists what still needs a
human in real Obsidian — renderer fidelity, performance, and cross-browser checks.
Design notes and the implementation plan live in docs/superpowers/.
npm version patch # bumps package.json, manifest.json, versions.json together
git push --follow-tags
Pushing the tag runs the release workflow, which verifies the tag matches
manifest.json, builds, and attaches main.js and manifest.json to a GitHub
release — the layout Obsidian expects.
MIT © 朱劭恩