[ANN] Snapshot.jl — interactive Pluto exports without a Julia server

FYI, I tried this on a fedora 42 machine in Chrome and FireFox and this was not working either. I see a note that say that sliders are static in this export. As note, I get warnings like this:

julia> html_path = Snapshot.export_notebook(
           notebook_path;
           single_file=true,
       )
┌ Warning: 🏝️ bond group degraded to fallback
│   url_path = "fractals.jl"
│   bonds =
│    1-element Vector{Symbol}:
│     :c
│   reasons =
│    1-element Vector{String}:
│     "Node verification failed globally: node failed: WebAssembly.compile(): Unknown type code 0x50, enable with --experimental-wasm-gc @+12\n"
└ @ Snapshot ~/.julia/packages/Snapshot/Z9Nh5/src/exporter.jl:235
┌ Warning: 🏝️ bond group degraded to fallback
│   url_path = "fractals.jl"
│   bonds =
│    2-element Vector{Symbol}:
│     :color1
│     :color2
│   reasons =
│    1-element Vector{String}:
│     "Node verification failed globally: node failed: WebAssembly.compile(): Unknown type code 0x50, enable with --experimental-wasm-gc @+12\n"

Oh those node warnings are super helpful! I am not sure if that will fix @VivMendes’s problem but I can likely package this Snapshot.jl with a node jll to make sure you dont run into old unsupported node issues, which I think is what you are seeing in those warnings @TheCedarPrince – one second and I will push another update to that same PR for a retry

Ok @TheCedarPrince, I added a bundled Node runtime to that PR to hopefully address the WasmGC verification issue you encountered. Could you try again from a fresh environment?

import Pkg
Pkg.activate(; temp=true)
Pkg.add(
    url="https://github.com/GroupTherapyOrg/Snapshot.jl",
    rev="fix/static-preview",
)

using Snapshot
import Downloads

test_dir = joinpath(homedir(), "SnapshotTest")
mkpath(test_dir)

notebook_path = joinpath(test_dir, "fractals.jl")

Downloads.download(
    "https://raw.githubusercontent.com/GroupTherapyOrg/Snapshot.jl/main/test/notebooks/featured/fractals.jl",
    notebook_path,
)

html_path = Snapshot.export_notebook(
    notebook_path;
    single_file=true,
)

println("Open this file in your browser:")
println(html_path)

Then open the printed fractals.html path directly. No local server or separate Node installation should be needed. Please let me know whether the sliders update the plots and share the export warnings if they do not.

I tested your latest code, and now it works on a Windows 11 machine. Despite the fact that it took around 4/5 minutes on a good computer to run (there seemed to exist some minor cache problem, see below), everything is now working well.

julia> html_path = Snapshot.export_notebook(
           notebook_path;
           single_file=true,
       )
┌ Info: 🏝️ island compiled
│   url_path = "fractals.jl"
│   bonds = 1-element Vector{Symbol}: …
│   cells = 3
│   failed_cells = 0
│   wasm_kb = 167.1
└   oracle_samples = 0
┌ Info: 🏝️ island compiled
│   url_path = "fractals.jl"
│   bonds = 2-element Vector{Symbol}: …
│   cells = 1
│   failed_cells = 0
│   wasm_kb = 19.7
└   oracle_samples = 5
┌ Info: 🏝️ island compiled
│   url_path = "fractals.jl"
│   bonds = 3-element Vector{Symbol}: …
│   cells = 3
│   failed_cells = 0
│   wasm_kb = 17.4
└   oracle_samples = 5
[ Info: Precompiling PlutoImageCoordinatePicker [79686372-6169-7274-6170-6568746b6366](cache misses: wrong dep version loaded (1))

This is great news! So it seems like the issue is with older NodeJS versions that do not enable WasmGC by default, which were causing subtle errors since snapshot.jl right now does not bundle NodeJS within the library.

I am attempting to patch this for good in that PR by adding a proper JLL bundle for the proper NodeJS 24 LTS – NodeJS_24: add v24.18.0 LTS - Pull Request #14252#discussion_r3626540499 - JuliaPackaging/Yggdrasil - GitHub

The compilation might take a while regardless for now though, I am focused mostly on correctness and optimizations will come later

I also tested it on a Linux machine (Ubuntu), and it works perfectly well. Congrats, this is really a fantastic package.

I use Marimo.py regularly as well. If I want to deploy a Wasm HTML file compiled by Marimo on a website or on an educational platform (like Moodle), I have to attach all the associated files created by the compilation process.

In the case of the Wasm HTML file compiled by Snapshot, it takes just ONE file: the original notebook compiled to WebAssembly. For educational purposes, this is amazing.

Glad it is working now, and thanks for helping me catch this bug! I pushed a fix so the newest Snapshot.jl release bundles its own Node.js runtime for export-time verification. It should no longer depend on whatever Node version happens to be installed on the machine.

I also documented the three main export options:

using Snapshot

# Recommended: lean Therapy-compatible export
export_notebook("notebook.jl")

# One self-contained interactive HTML file
export_notebook("notebook.jl"; single_file=true)

# Classic full-Pluto export
export_notebook("notebook.jl"; therapy=false)

The single-file option produces an HTML file that can be copied and opened directly, as you have already tested. For a larger website, the default directory export is more efficient because browsers can cache its separate assets.

I also added an example showing how to publish those exported files through Therapy.jl—for example, as a standalone interactive site on GitHub Pages:

using Therapy

app = App(
    routes_dir="routes",
    components_dir="components",
    output_dir="dist",
    tailwind=false,
)

staticfiles(app, "public/notebook", "notebook")
Therapy.build(app)

Snapshot performs the notebook export during the build, and Therapy assembles the static site. No Julia server is needed after deployment. The complete setup and explanation are available in the Snapshot documentation (How It Works - Snapshot.jl).

I am excited about where this can go, but I also want to be clear about its current limits. The main bottleneck is WasmTarget.jl, which still supports only a relatively strict subset of Julia and remains early and lightly tested. I cannot make sweeping compatibility promises until WasmTarget becomes substantially more robust.

That being said, I am having a lot of fun building this, and I am glad you were able to get it working on Windows. Your testing was genuinely helpful—thank you!

Hi. I have a notebook that involves an exam with multiple-choice questions that I am trying to compile into Wasm. There is nothing very intricate in this notebook, and the packages it uses are:

CSV = "~0.10.16"
DataFrames = "~1.8.2"
HypertextLiteral = "~1.0.0"
NLsolve = "~4.5.1"
PlutoPlotly = "~0.6.6"
PlutoTeachingTools = "~0.4.7"
PlutoUI = "~0.7.83"
StatsBase = "~0.34.11"

When I tried to compile it, I got the following errors (please see the image below, sorry for its size):

I also get a lot of errors like these:

I hope this information provides any clues about the main culprits in the failed compilation. Is there anything I can do in my notebook, or must I just wait for further developments in your WasmTarget.jl package?

Thanks.

Plotting likely wont work unless you are using WasmMakie.jl for right now

The NLSolve.jl is tough too – that being said, if you want to file an issue directly on WasmTarget.jl with the src notebook code, I can take a look

OK, I hadn’t realized that WasmMakie.jl would be the only option to convert plotting into a Wasm HTML file. Thanks for clarifying this. As far as NLsolve.jl is concerned, I actually don’t need it in most of my notebooks (I checked, and in fact it was not used in this specific notebook).

In my job, I have to use Python and Julia depending on the circumstances. That is why most of my plotting has been done based on Plotly (very easy to jump from one language to the other). At this moment, it would be a huge task to adapt a large number of notebooks to a new plotting package.

Anyway, thanks for your kind feedback. I will continue to pay close attention to your packages, which look extremely useful and are a real fresh breeze in the Julia ecosystem.

@VivMendes if you end up with a simple workflow for going from a Pluto notebook to something that works in Moodle, could you please share it?

Yes, sure. But the answer depends on what you intend to do in Moodle, and it also depends on what Pluto mode you have in mind.

Firstly, to run an active notebook (Jupyter, Pluto, Slate, Marimo, Observable) inside Moodle, you will need to have a JupyterHub server integrated into Moodle as an App. I have been told by the IT people at my institution that for a large number of users (more than 100), this option is not only very expensive but also very difficult to manage. We have given up this option.

Secondly, if you want to use a fully reproducible, self-contained HTML file produced from some available notebook (Pluto, Slate, Marimo, etc.), without using the secure protection of the Safe Exam Browser, and you want to post it in Moodle, that is very easy to do: just copy the HTML file and insert it as a “File” under the resource items. Nothing else will be required. For Marimo, you must paste the zipped folder into the “File” item, unzip it, and choose the file to be used as the main file in that folder.

Thirdly, if you want to use the secured environment provided by the Safe Exam Browser (SEB), that will be another story. By default, in Moodle, SEB can only be used under the activity “Quiz”. Obviously, SEB can be configured to be used with other activities and/or resources in the Moodle platform, but that looks more complicated than configuring SEB to be used outside Moodle and running a self-contained HTML file under this secured environment.

Fourthly, you deploy your self-contained HTML file in some place of your choice (server, website), deliver the configured .seb file to your students, and the exam/test will run on their computers, protected by a secure browser session. Students only need to have SEB installed on their computers; click the .seb file you provide, and the file launches SEB, which launches the self-contained HTML file.