Warning: Most of the text and code in this repository has been created with heavy LLM-use. If you want to read an introduction written by a human (me), please start here.
Anywidget Experiments¶
Custom anywidget widgets that interoperate, render statically without a kernel, and play nicely with lonboard.
Live demo: https://
Background: What is this and why — the motivation, what’s weird about it, and what it isn’t.
The pages on the live site are static HTML — no kernel running, no server, just a mystmd build. Click the buttons, scrub the counters, watch lonboard’s points resize. All of it is JS-only at runtime.
Walkthrough¶
An anywidget, statically — a single
CounterWidget. Click+/-/Reset; the count updates locally with no kernel.Anywidgets talking to each other — two widgets on the same page, wired through a shared in-page registry.
Lonboard, statically — a minimal
lonboard.Maprendered with binary Parquet buffers preserved through MyST’s build pipeline.Lonboard ↔ anywidget interop — a counter drives a lonboard layer’s point radius via a generic binder widget.
Showcase notebooks¶
These two stretch the toolchain into something more polished — real data, custom UI, and the kind of “wow, that’s a notebook?” reaction we wanted to demonstrate. Both ship as static pages, no kernel.
What happened to your name? — type your name. A 144-year SSA-baby-names trajectory animates across a sweeping playhead, your birth year is marked, four “trajectory twins” appear below, and a small surprise fires when the playhead hits your name’s peak year. The custom
NameExplorerwidget is one ~500-line vanilla-JS file with an inlined confetti routine and an Easter egg.Hurricane Helene, 48 hours later — Asheville disaster-response demo. A lonboard map with MODIS basemap, hand-authored Sentinel-1-style flood polygon, OSM building footprints (~41K), and hospitals. The
HurricaneDashboardwidget toggles layers and drives a flood-depth threshold slider that filters which buildings are shown — counters tween live as you drag.
These two notebooks pull external data (SSA names ZIP via Wayback, OSM via Overpass, MODIS via NASA GIBS, NOAA HURDAT2). The fetched files are .gitignored. To regenerate them locally before re-executing the notebooks, run bash data/names/fetch.sh and python data/helene/fetch.py. CI does not re-execute these — their widget state is committed in the .ipynb outputs, so a normal myst build is enough on push.
How it works¶
A small mystmd plugin at plugins/anywidget-static-export.mjs rewrites notebook widget outputs into AST nodes the @myst-theme/anywidget renderer can mount, with a runtime shim that no-ops the kernel-sync calls and hydrates binary buffers (Apache Parquet for lonboard) at page load. The plugin’s README has the full catalogue of workarounds — they touch mystmd, myst-theme, @jupyter-widgets/base, and lonboard itself — and the docs/ folder has a write-up per upstream fix.
Local development¶
git clone https://github.com/batpad/anywidget-experiments.git
cd anywidget-experiments
python -m venv venv
source venv/bin/activate
pip install -r requirements.txt
npm install
npm run build # runs prebuild (executes lonboard notebooks via nbclient) then myst build
npm run serve # python -m http.server on _build/htmlFor a development loop on a single notebook:
jupyter lab notebooks/04_lonboard_interop.ipynb
# edit, "Restart Kernel and Run All", save with widget state, then `npm run build`Repo structure¶
notebooks/ Walkthrough (01–04) + showcase (05–06) notebooks.
widgets/ CounterWidget, LinkedCounterWidget, WidgetBinder, NameExplorer, HurricaneDashboard.
plugins/ The static-export mystmd plugin + README explaining each hack.
data/ Datasets fetched at prebuild for the showcase notebooks (mostly .gitignored — see data/*/fetch.py / fetch.sh).
docs/ Per-upstream-fix write-ups + parking-lot example-ideas-*.md for sister notebooks.
.github/ GitHub Actions workflow that builds + deploys the site to Pages on every push to main.Acknowledgments¶
lonboard by Development Seed
mystmd and myst-theme by Jupyter Book
License¶
MIT