concept-collection / jupyterlite-numbl-kernel
jupyterlite-numbl-kernel / README.md
5.8 KBPreviewCodeBlameHistoryRaw

jupyterlite-numbl-kernel#

Run numbl — numerical computing with MATLAB syntax — in JupyterLite notebooks, entirely in the browser. No server, no kernel process, nothing for the reader to install.

numbl is an open-source numerical-computing engine, written in TypeScript, that uses MATLAB syntax, so .m code runs unchanged. This kernel runs a numbl session in a Web Worker on the page: variables persist across cells, console output streams into the running cell, plots render as figures (including interactive 3-D), and .m files next to the notebook are part of the workspace (named functions, called from cells). Everything runs client-side.

Demo: https://concept-collection.github.io/jupyterlite-numbl-kernel/ (deployed from this repo via GitHub Pages)

Why#

A numbl notebook can be a static web page: hostable on GitHub Pages, shareable as a link, and runnable by anyone with a browser. Because numbl uses MATLAB syntax and needs no MATLAB/Octave install (or any server), the same .m code that would otherwise sit behind a licensed product just runs in the tab.

How it works#

Three small pieces, all in src/:

Build a site with it#

pip install jupyterlite-core jupyterlite-numbl-kernel
jupyter lite build --contents my-notebooks --output-dir dist
# dist/ is a static site; serve it anywhere

.m files next to a notebook are synced into the session recursively, so MATLAB's folder-based +namespace/, @class/, and private/ layouts work as expected.

The demo/ directory is the source of the demo site above — a numbered walkthrough of the language and the MATLAB object model (classes, namespaces, packages). .github/workflows/deploy.yml builds and deploys it to GitHub Pages.

Fresh content on redeploy (demo choice)#

JupyterLite caches notebooks in the browser (IndexedDB) and can keep serving stale copies from its service worker even after a redeploy. Because this is a demo, demo/jupyter-lite.json uses in-memory storage and disables the service worker, so every reload re-seeds the latest deployed notebooks. The trade-off is that a visitor's edits last only for the session. For a real deployment where users keep their work, drop those settings and use JupyterLite's default persistent storage.

Cross-origin isolation (for interrupt and input)#

The Stop button and input() both rely on a SharedArrayBuffer, which browsers expose only on a cross-origin-isolated page (COOP/COEP headers). GitHub Pages can't set those headers, so the demo ships a small service worker, demo/coi-serviceworker.js (based on coi-serviceworker), that synthesizes them client-side; demo/inject-coi.mjs wires it into every generated page during the build. Without isolation, interrupt and input() degrade gracefully (see Limitations).

Limitations (proof of concept)#

Development#

Requires Python ≥ 3.9, Node ≥ 20, and numbl >= 0.4.16.

python -m venv .venv && source .venv/bin/activate
pip install "jupyterlab~=4.6.0" "jupyterlite-core==0.8.1"

jlpm install
jlpm build            # tsc + labextension (dev)
pip install -e .      # editable install, registers the labextension

# Build and serve the demo site locally
pip install -r demo/requirements.txt
jupyter lite build --lite-dir demo --contents content --output-dir demo/_output
node demo/inject-coi.mjs demo/_output   # cross-origin isolation, for interrupt
python -m http.server -d demo/_output 8000   # then open http://localhost:8000

jlpm watch rebuilds on change during development.

License#

Apache-2.0. Built on numbl and JupyterLite; scaffolded from the jupyterlite/echo-kernel template.