A Pluto notebook becomes a Therapy component
Snapshot takes a Pluto.jl notebook and emits a lean Therapy component — supported interactive sliders and plots run in the browser as WebAssembly, with no Julia server, no Pluto frontend, and no multi-megabyte statefile. It drops into any static host as a standalone page, or into a Therapy app as native DOM — themeable, with or without reactivity.
The problem it solves
Pluto's classic static export is faithful but heavy: it bakes the entire session state (~2.6 MB) into the HTML and ships the full Pluto frontend to re-hydrate it. For a page of sliders and a plot, that's a lot of bytes — and it locks the look to Pluto's chrome.
The lean export keeps rendered notebook content and verified interactions, while omitting Pluto's editor and session machinery. For notebooks whose interactions compile, this can substantially reduce frontend and session-state overhead.
The pipeline
At export time the notebook runs once in Pluto. Then, per notebook:
Extract the reactive graph
Pluto already knows the dependency graph + which cells depend on a @bind widget. Snapshot groups co-dependent bond cells and lifts each dependent cell into a pure function.
Compile to WebAssembly islands
Each bond group compiles to a small WasmGC module via WasmTarget.jl — verified at export time against real notebook re-runs before it ships.
Server-render every cell
Each cell's output is written straight into the page as HTML, following Pluto's own decisions about ordering, output, and visibility. Nothing structural is invented.
Hydrate with signals + islands
A tiny runtime wires each <bond> input to the wasm island that owns it. Move a slider and only the dependent cells recompute — figures redraw on a live <canvas>, numbers update in place.
Why it maps cleanly
Pluto's reactive model and Therapy's signals model are the same idea, so the translation is direct:
Theming: copy Pluto, swap the variables
The export ports Pluto's own output CSS almost verbatim — markdown, admonitions, tables, code, the tree viewer, the table of contents — and re-points every Pluto color variable at a DaisyUI token.
So the notebook renders exactly as Pluto does, but one attribute — data-theme="…" on <html> — restyles the whole site at once. The 🎨 picker in the nav sets it: because each notebook is native DOM (no iframe), the chrome and every notebook reskin together, seamlessly, with one click.
Plain native controls with no authored class or inline style inherit those theme tokens too. A class, inline style, or data-snapshot-unstyled leaves the control's appearance with the notebook author.
Two delivery formats
Portable HTML
Embed Snapshot's runtime and WASM modules in one file. Double-click it, copy it, or upload it as a single artifact.
export_notebook("notebook.jl";
single_file=true)Static directory
Keep the smaller HTML and its .islands directory together on GitHub Pages, S3, or any ordinary static host.
export_notebook("notebook.jl")Direct opening and static hosting
The efficient directory export uses neighboring WebAssembly and JSON files, which browsers intentionally block when its HTML is opened through a file:// URL. Serve that directory through any static host.
For the simplest local handoff, pass single_file=true. Snapshot embeds its generated runtime assets into the document, so the resulting HTML can be opened directly without running a local server. This is convenient for downloads and platforms that accept active HTML, but base64 increases binary size, the browser cannot cache individual modules, and a host may still block scripts or WebAssembly. Use the split format for a deployed site.
For a Therapy site, use a small Snapshot build environment to export into the site's public directory, then a separate Therapy environment to mount it with staticfiles(app, "public/notebook", "notebook"). The separate environments are intentional because Snapshot's Pluto stack and Therapy currently resolve different HTTP major versions. julia --project=therapy-site therapy-site/app.jl dev serves it locally; julia --project=therapy-site therapy-site/app.jl build copies the complete mount into the site's static output and writes .nojekyll for GitHub Pages. Set Therapy's base_path="/REPOSITORY" for a project Pages URL. Snapshot is needed at build time only; the deployed site has no Julia server. Mount only trusted notebook HTML on an application's origin; publish untrusted exports on a separate origin.
Browser compatibility
Snapshot's current WasmTarget output uses WasmGC and WebAssembly JavaScript string builtins. Use Chrome or Edge 130+, Firefox 134+, or another browser with equivalent support. Safari does not currently implement the required string builtins. This is a browser-engine requirement, not an operating-system restriction.
Snapshot's export verifier is the bundled Node 24 JLL, never a Node executable from PATH. Export is currently available on 64-bit Windows; x86_64 and ARM64 macOS; x86_64 and ARM64 Linux with glibc or musl; and powerpc64le Linux with glibc. Generated HTML can be viewed on any operating system with a compatible browser.
Trust boundary
Exporting executes the notebook and its package environment with the permissions of the Julia process. Snapshot is an exporter, not a sandbox. Build only trusted notebooks locally; use a disposable, network-restricted runner without credentials for community-supplied code. Generated HTML is author-controlled content and should be served from an origin isolated from authenticated applications.
The package works on any static host. snapshot.show is a separate hosted service, not a runtime dependency of Snapshot.jl.
The classic export (not recommended)
Passing therapy=false explicitly selects the original full-Pluto export — Pluto's standard static HTML plus a JavaScript bundle that hydrates the @bind cells in Pluto's static export, alongside Snapshot's island assets. It still works:
export_notebook("notebook.jl"; therapy=false)See it in action
Every notebook in the gallery is a live Therapy component. Try the theme picker.