/ concept-collection / jupyterlite-numbl-kernel
Sign in
concept-collection / jupyterlite-numbl-kernel
jupyterlite-numbl-kernel / README.md
159 lines · 7.1 KBPreviewCodeBlameHistoryRaw
1# jupyterlite-numbl-kernel
3Run [**numbl**](https://numbl.org), numerical computing with **MATLAB
4syntax**, in [JupyterLite](https://jupyterlite.readthedocs.io/) notebooks,
5**entirely in the browser**, with no server, no kernel process, and nothing
6for the reader to install.
8numbl is an open-source numerical-computing engine, written in TypeScript,
9that uses MATLAB syntax, so `.m` code runs unchanged. This kernel runs a
10numbl session in a Web Worker in the page: variables persist across cells,
11console output streams into the running cell, plots render as figures in cell
12outputs (including interactive 3-D), the `mip` package manager can install
13numbl packages from GitHub, and `.m` files next to the notebook are part of
14the workspace (named functions, called from cells). Everything runs
15client-side.
17**Demo site:**
18<https://concept-collection.github.io/jupyterlite-numbl-kernel/> (deployed
19from this repo via GitHub Pages; see `.github/workflows/deploy.yml`)
21## Why
23This kernel is a proof of concept that a numbl notebook can be a static web
24page: hostable on GitHub Pages, shareable as a link, and executable by anyone
25with a browser. Because numbl uses MATLAB syntax and needs no MATLAB/Octave
26install (or any server process), the same `.m` code that would otherwise
27require a licensed product behind a server just runs in the tab.
29## How it works
31Three small pieces, all in this repo:
33- **Kernel** (`src/kernel.ts`): implements JupyterLite's `BaseKernel` from
34 `@jupyterlite/services`. Before each `execute_request`, `.m` files in the
35 notebook's directory (read via the JupyterLite contents manager) are
36 synced into the numbl session; the cell source then runs against the
37 session's persistent workspace (`createNumblSession` /
38 `session.execute` from `numbl/browser`, a Web Worker that numbl manages).
39 Output streams back as `stream` messages; the run's plot instructions are
40 published as `display_data` with the mime type
41 `application/vnd.numbl.figure+json`.
42- **Figure renderer** (`src/mime.tsx`): a JupyterLab mime renderer for that
43 mime type: it replays the instructions through numbl's figures reducer and
44 mounts numbl's React `FigureView` (from `numbl/graphics`). Outputs are
45 plain JSON, so saved notebooks re-render wherever the extension is
46 installed.
47- **Kernel registration** (`src/index.ts`): registers the kernelspec with
48 JupyterLite's `IKernelSpecs`.
50## Build a site with it
52```bash
53pip install jupyterlite-core jupyterlite-numbl-kernel
54jupyter lite build --contents my-notebooks --output-dir dist
55# dist/ is a static site; serve it anywhere
56```
58The `demo/` directory in this repo contains the demo site sources
59(notebooks + requirements); `.github/workflows/deploy.yml` builds and
60deploys it to GitHub Pages. `demo/content/` is a numbered walkthrough: an
61intro (with plotting), a systematic language tour (data types, matrices,
62control flow, linear algebra, data structures, numerical methods,
63plotting), the advanced MATLAB object model (classes and OOP, namespaces
64and packages), and finally installing packages with `mip`. The class and
65package `.m` files live next to the notebooks (e.g. `Vec2.m`, `+geom/`,
66`@Poly/`) and are synced into the session recursively.
68### Always-fresh content (demo choice)
70JupyterLite caches content in two layers that both defeat redeploys:
721. It copies notebooks into the browser's IndexedDB on first visit, and that
73 local copy then wins over the deployed files **even after a redeploy**.
742. Its **service worker** is an offline caching proxy for the app itself and
75 for content files, so it can keep serving the old app and old notebooks
76 after a redeploy until it happens to update.
78Since this is a demo, `demo/jupyter-lite.json` neutralizes both: it uses
79JupyterLite's in-memory storage (so every reload re-seeds the latest
80deployed notebooks) and disables the service-worker plugin (which the numbl
81kernel doesn't need, since it reads content on the main thread, not via the
82service worker's kernel drive):
84```json
86 "jupyter-config-data": {
87 "enableMemoryStorage": true,
88 "contentsStorageDrivers": ["memoryStorageDriver"],
89 "settingsStorageDrivers": ["memoryStorageDriver"],
90 "workspacesStorageDrivers": ["memoryStorageDriver"],
91 "disabledExtensions": [
92 "@jupyterlite/application-extension:service-worker-manager"
93 ]
94 }
96```
98The trade-off is that a visitor's edits live only for the session and are
99discarded on reload. A visitor who loaded the site **before** the service
100worker was disabled still has it registered and must clear browser data
101(or `Help > Clear Browser Data`) once to get past it. For a real deployment where users should keep their
102work, omit these keys (the default persistent storage) and bump
103`contentsStorageName` when you want to force-refresh shipped content.
104numbl's own package cache (installed via `mip`) lives in a separate
105IndexedDB store and is unaffected, so `mip`-installed packages still
106persist across reloads.
108## Limitations (proof of concept)
110- **No interrupt**: a runaway cell can only be stopped by restarting the
111 kernel (restart works and gives a fresh workspace). Cooperative
112 cancellation exists in numbl but needs `SharedArrayBuffer`, i.e.
113 cross-origin isolation headers, which plain GitHub Pages doesn't set.
114- **No `input()`** (stdin), for the same reason.
115- **Figures are per-cell** (like inline matplotlib): each cell renders the
116 figures its own commands produce; `hold on` does not span cells.
117- **Named function definitions are not supported inside cells** (a numbl
118 REPL limitation): anonymous functions work, and named functions belong in
119 `.m` files next to the notebook (see `demo/content/statsutils.m`), which
120 this kernel syncs into the session automatically.
121- The `.m`-file sync is **one-way**: deleting a `.m` file from the file
122 browser leaves its function defined until the kernel restarts, and files
123 written by cell code (e.g. via `fopen`) don't appear back in the file
124 browser.
125- **uihtml** components render display-only; the MATLAB↔HTML event bridge
126 is not wired into outputs yet.
127- numbl itself is not MATLAB: it covers a large, tested subset of the
128 language and toolbox surface. See [numbl](https://numbl.org) for scope.
130## Development
132Requires Python ≥ 3.9 and NodeJS ≥ 20, and `numbl >= 0.4.14` on npm (the
133first release with the incremental `session.execute` browser API). To
134develop against an unreleased numbl checkout, run `npm pack` there and
135point the `numbl` dependency at the tarball.
137```bash
138python -m venv .venv && source .venv/bin/activate
139pip install "jupyterlab~=4.6.0" "jupyterlite-core==0.8.1"
141jlpm install
142jlpm build # tsc + labextension (dev)
143pip install -e . # editable install, registers the labextension
145# Build and serve the demo site locally
146pip install -r demo/requirements.txt
147jupyter lite build --lite-dir demo --contents content --output-dir demo/_output
148python -m http.server -d demo/_output 8000
149```
151`jlpm watch` rebuilds on change during development.
153## License
155Apache-2.0. Built on [numbl](https://numbl.org) and
156the [JupyterLite](https://github.com/jupyterlite/jupyterlite) kernel API;
157scaffolding follows the
158[jupyterlite/echo-kernel](https://github.com/jupyterlite/echo-kernel)
159template.
moveopenescclose