Snapshot.jl
How it works

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:

1

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.

2

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.

3

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.

4

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:

@bind x Slider(…)
signal + <input>
a shared reactive value the widget writes + cells read
bond-dependent cell
island / effect
wasm recomputes it when its inputs change
static cell
SSR HTML
rendered once at export, no runtime
figure output
live <canvas>
redrawn by WasmMakie, not a baked image
text / number
reactive DOM node
updated in place — divs, not canvas

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 ways to use it

Standalone page

Export a notebook and serve the HTML anywhere — GitHub Pages, S3, a CDN. No server.

export_notebook("notebook.jl")
# → notebook.html + .islands/

Component in a Therapy app

Embed the notebook inside a larger Therapy site and let the host's theme drive it. This gallery does exactly that.

export_notebook("notebook.jl";
    therapy=true,
    theme_picker=false)

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)
Not the recommended path: it ships the full Pluto frontend plus a baked ~2.6 MB statefile (much heavier), isn't a native Therapy component, and isn't as heavily tested. The default Therapy export is the supported path unless you specifically need the classic export.

See it in action

Every notebook in the gallery is a live Therapy component. Try the theme picker.

Browse the Notebooks →

Pluto notebooks as lean Therapy components — interactive WebAssembly, no server.