41c072cjupyterlite-numbl-kernel: MATLAB-syntax kernel for JupyterLiteJeremy Magland 1# jupyterlite-numbl-kernel
3A MATLAB-syntax kernel for [JupyterLite](https://jupyterlite.readthedocs.io/) —
4notebooks that run **entirely in the browser**, with no server, no kernel
5process, and nothing for the reader to install.
7The language engine is [numbl](https://github.com/flatironinstitute/numbl), an
8open-source MATLAB-syntax implementation in TypeScript. Each kernel runs a
9numbl session in a Web Worker in the page: variables persist across cells,
10console output streams into the running cell, MATLAB plotting commands render
6b31a9aSync .m workspace files from the notebook directory into the sessionJeremy Magland 11as figures in cell outputs (including interactive 3-D), the `mip` package
12manager can install MATLAB-syntax packages from GitHub, and `.m` files next
13to the notebook are part of the workspace (named functions, called from
14cells) — all client-side.
16**Demo site:**
17<https://concept-collection.github.io/jupyterlite-numbl-kernel/> (deployed
18from this repo via GitHub Pages — see `.github/workflows/deploy.yml`)
20## Why
22Existing MATLAB/Octave Jupyter kernels require the real product installed
23behind a server. This kernel is a proof of concept that a MATLAB-syntax
24notebook can be a static web page: hostable on GitHub Pages, shareable as a
25link, and executable by anyone with a browser.
27## How it works
29Three small pieces, all in this repo:
31- **Kernel** (`src/kernel.ts`) — implements JupyterLite's `BaseKernel` from
6b31a9aSync .m workspace files from the notebook directory into the sessionJeremy Magland 32 `@jupyterlite/services`. Before each `execute_request`, `.m` files in the
33 notebook's directory (read via the JupyterLite contents manager) are
34 synced into the numbl session; the cell source then runs against the
35 session's persistent workspace (`createNumblSession` /
36 `session.execute` from `numbl/browser`, a Web Worker that numbl manages).
37 Output streams back as `stream` messages; the run's plot instructions are
38 published as `display_data` with the mime type
39 `application/vnd.numbl.figure+json`.
41c072cjupyterlite-numbl-kernel: MATLAB-syntax kernel for JupyterLiteJeremy Magland 40- **Figure renderer** (`src/mime.tsx`) — a JupyterLab mime renderer for that
41 mime type: it replays the instructions through numbl's figures reducer and
42 mounts numbl's React `FigureView` (from `numbl/graphics`). Outputs are
43 plain JSON, so saved notebooks re-render wherever the extension is
44 installed.
45- **Kernel registration** (`src/index.ts`) — registers the kernelspec with
46 JupyterLite's `IKernelSpecs`.
48## Build a site with it
50```bash
51pip install jupyterlite-core jupyterlite-numbl-kernel
52jupyter lite build --contents my-notebooks --output-dir dist
53# dist/ is a static site — serve it anywhere
54```
56The `demo/` directory in this repo contains the demo site sources
57(notebooks + requirements); `.github/workflows/deploy.yml` builds and
58deploys it to GitHub Pages.
8befa62Demo: use in-memory JupyterLite storage so reloads show fresh contentJeremy Magland 60### Always-fresh content (demo choice)
62By default JupyterLite copies notebooks into the browser's IndexedDB on
63first visit, and that local copy then wins over the deployed files **even
64after a redeploy** — so returning visitors keep seeing stale content. Since
65this is a demo, `demo/jupyter-lite.json` opts into JupyterLite's in-memory
66storage so every page reload re-seeds the latest deployed notebooks:
68```json
69{
70 "jupyter-config-data": {
71 "enableMemoryStorage": true,
72 "contentsStorageDrivers": ["memoryStorageDriver"],
73 "settingsStorageDrivers": ["memoryStorageDriver"],
74 "workspacesStorageDrivers": ["memoryStorageDriver"]
75 }
76}
77```
79The trade-off is that a visitor's edits live only for the session and are
80discarded on reload. For a real deployment where users should keep their
81work, omit these keys (the default persistent storage) and bump
82`contentsStorageName` when you want to force-refresh shipped content.
83numbl's own package cache (installed via `mip`) lives in a separate
84IndexedDB store and is unaffected, so `mip`-installed packages still
85persist across reloads.
41c072cjupyterlite-numbl-kernel: MATLAB-syntax kernel for JupyterLiteJeremy Magland 87## Limitations (proof of concept)
89- **No interrupt**: a runaway cell can only be stopped by restarting the
90 kernel (restart works and gives a fresh workspace). Cooperative
91 cancellation exists in numbl but needs `SharedArrayBuffer`, i.e.
92 cross-origin isolation headers, which plain GitHub Pages doesn't set.
93- **No `input()`** (stdin), for the same reason.
94- **Figures are per-cell** (like inline matplotlib): each cell renders the
95 figures its own commands produce; `hold on` does not span cells.
96- **Named function definitions are not supported inside cells** (a numbl
97 REPL limitation) — anonymous functions work; named functions belong in
6b31a9aSync .m workspace files from the notebook directory into the sessionJeremy Magland 98 `.m` files next to the notebook (see `demo/content/statsutils.m`), which
99 this kernel syncs into the session automatically.
100- The `.m`-file sync is **one-way**: deleting a `.m` file from the file
101 browser leaves its function defined until the kernel restarts, and files
102 written by cell code (e.g. via `fopen`) don't appear back in the file
103 browser.
41c072cjupyterlite-numbl-kernel: MATLAB-syntax kernel for JupyterLiteJeremy Magland 104- **uihtml** components render display-only; the MATLAB↔HTML event bridge
105 is not wired into outputs yet.
106- numbl itself is not MATLAB: it covers a large, tested subset of the
107 language and toolbox surface. See the
108 [numbl repo](https://github.com/flatironinstitute/numbl) for scope.
110## Development
112Requires Python ≥ 3.9 and NodeJS ≥ 20, and `numbl >= 0.4.14` on npm (the
113first release with the incremental `session.execute` browser API). To
114develop against an unreleased numbl checkout, run `npm pack` there and
115point the `numbl` dependency at the tarball.
117```bash
118python -m venv .venv && source .venv/bin/activate
119pip install "jupyterlab~=4.6.0" "jupyterlite-core==0.8.1"
121jlpm install
122jlpm build # tsc + labextension (dev)
123pip install -e . # editable install, registers the labextension
125# Build and serve the demo site locally
126pip install -r demo/requirements.txt
127jupyter lite build --lite-dir demo --contents content --output-dir demo/_output
128python -m http.server -d demo/_output 8000
129```
131`jlpm watch` rebuilds on change during development.
133## License
135Apache-2.0. Built on [numbl](https://github.com/flatironinstitute/numbl) and
136the [JupyterLite](https://github.com/jupyterlite/jupyterlite) kernel API;
137scaffolding follows the
138[jupyterlite/echo-kernel](https://github.com/jupyterlite/echo-kernel)
139template.