jupyterlite-numbl-kernel#
Run numbl, numerical computing with MATLAB syntax, in JupyterLite notebooks, entirely in the browser, with no server, no kernel process, and 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 in the page: variables persist across cells,
console output streams into the running cell, plots render as figures in cell
outputs (including interactive 3-D), the mip package manager can install
numbl packages from GitHub, and .m files next to the notebook are part of
the workspace (named functions, called from cells). Everything runs
client-side.
Demo site:
https://concept-collection.github.io/jupyterlite-numbl-kernel/ (deployed
from this repo via GitHub Pages; see .github/workflows/deploy.yml)
Why#
This kernel is a proof of concept that a numbl notebook can be a static web
page: hostable on GitHub Pages, shareable as a link, and executable by anyone
with a browser. Because numbl uses MATLAB syntax and needs no MATLAB/Octave
install (or any server process), the same .m code that would otherwise
require a licensed product behind a server just runs in the tab.
How it works#
Three small pieces, all in this repo:
- Kernel (
src/kernel.ts): implements JupyterLite'sBaseKernelfrom@jupyterlite/services. Before eachexecute_request,.mfiles in the notebook's directory (read via the JupyterLite contents manager) are synced into the numbl session; the cell source then runs against the session's persistent workspace (createNumblSession/session.executefromnumbl/browser, a Web Worker that numbl manages). Output streams back asstreammessages; the run's plot instructions are published asdisplay_datawith the mime typeapplication/vnd.numbl.figure+json. - Figure renderer (
src/mime.tsx): a JupyterLab mime renderer for that mime type: it replays the instructions through numbl's figures reducer and mounts numbl's ReactFigureView(fromnumbl/graphics). Outputs are plain JSON, so saved notebooks re-render wherever the extension is installed. - Kernel registration (
src/index.ts): registers the kernelspec with JupyterLite'sIKernelSpecs.
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
The demo/ directory in this repo contains the demo site sources
(notebooks + requirements); .github/workflows/deploy.yml builds and
deploys it to GitHub Pages. The demo/content/advanced/ folder holds a
systematic seven-notebook tour of the language (data types, matrices,
control flow, linear algebra, data structures, numerical methods, and
plotting).
Always-fresh content (demo choice)#
JupyterLite caches content in two layers that both defeat redeploys:
- It copies notebooks into the browser's IndexedDB on first visit, and that local copy then wins over the deployed files even after a redeploy.
- Its service worker is an offline caching proxy for the app itself and for content files, so it can keep serving the old app and old notebooks after a redeploy until it happens to update.
Since this is a demo, demo/jupyter-lite.json neutralizes both: it uses
JupyterLite's in-memory storage (so every reload re-seeds the latest
deployed notebooks) and disables the service-worker plugin (which the numbl
kernel doesn't need, since it reads content on the main thread, not via the
service worker's kernel drive):
{
"jupyter-config-data": {
"enableMemoryStorage": true,
"contentsStorageDrivers": ["memoryStorageDriver"],
"settingsStorageDrivers": ["memoryStorageDriver"],
"workspacesStorageDrivers": ["memoryStorageDriver"],
"disabledExtensions": [
"@jupyterlite/application-extension:service-worker-manager"
]
}
}
The trade-off is that a visitor's edits live only for the session and are
discarded on reload. A visitor who loaded the site before the service
worker was disabled still has it registered and must clear browser data
(or Help > Clear Browser Data) once to get past it. For a real deployment where users should keep their
work, omit these keys (the default persistent storage) and bump
contentsStorageName when you want to force-refresh shipped content.
numbl's own package cache (installed via mip) lives in a separate
IndexedDB store and is unaffected, so mip-installed packages still
persist across reloads.
Limitations (proof of concept)#
- No interrupt: a runaway cell can only be stopped by restarting the
kernel (restart works and gives a fresh workspace). Cooperative
cancellation exists in numbl but needs
SharedArrayBuffer, i.e. cross-origin isolation headers, which plain GitHub Pages doesn't set. - No
input()(stdin), for the same reason. - Figures are per-cell (like inline matplotlib): each cell renders the
figures its own commands produce;
hold ondoes not span cells. - Named function definitions are not supported inside cells (a numbl
REPL limitation): anonymous functions work, and named functions belong in
.mfiles next to the notebook (seedemo/content/statsutils.m), which this kernel syncs into the session automatically. - The
.m-file sync is one-way: deleting a.mfile from the file browser leaves its function defined until the kernel restarts, and files written by cell code (e.g. viafopen) don't appear back in the file browser. - uihtml components render display-only; the MATLAB↔HTML event bridge is not wired into outputs yet.
- numbl itself is not MATLAB: it covers a large, tested subset of the language and toolbox surface. See numbl for scope.
Development#
Requires Python ≥ 3.9 and NodeJS ≥ 20, and numbl >= 0.4.14 on npm (the
first release with the incremental session.execute browser API). To
develop against an unreleased numbl checkout, run npm pack there and
point the numbl dependency at the tarball.
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
python -m http.server -d demo/_output 8000
jlpm watch rebuilds on change during development.
License#
Apache-2.0. Built on numbl and the JupyterLite kernel API; scaffolding follows the jupyterlite/echo-kernel template.