Reactive notebooks that compile. Drag something below — it's compiled Go running in your browser, no server.
Drag the sliders. Every downstream value recomputes; the cost/latency curve moves.
The thing you just dragged is this file. No framework import. No .ipynb. Nothing from this project — just Go and the standard library. A cell is a top-level function; the graph is its parameter names.
//go:notebook package capacity // Incoming jobs per hour. //notebook:slider min=0 max=5000 step=50 func arrivalRate() (lambda PerHour) { return 1200 } // Servers in the fleet. //notebook:slider min=1 max=256 func servers() (c int) { return 80 } // Offered load in Erlangs. `lambda` and `mu` wire in by name+type — that's the edge. func offeredLoad(lambda, mu PerHour) (a Erlangs) { return Erlangs(float64(lambda) / float64(mu)) } // Server utilization. `a` and `c` come from the cells above, by name. func utilization(a Erlangs, c int) (rho float64) { return float64(a) / float64(c) }
Drag a point, or the polynomial degree. The least-squares fit follows, live.
Dragging a control point is an edit to a leaf — the renderer reads it, the runtime writes it, a write is not an edge. No two-way binding to wire, because the cycle is unrepresentable.
Drag n up: the posterior over the line collapses onto the truth. Drag it back down — it re-widens, because nothing here is stateful.
Scrubbing n backward works because the posterior is a pure function of sufficient statistics — sums, not a fold. A fold could only go forward. The type checker and the graph make that the natural way to write it.
A Pyodide-based notebook ships 10 MB+ of interpreter before your code and cold-starts in seconds — because it ships an interpreter, and this ships a compiled program.
The honest caveat: in the browser, GOOS=js is single-threaded. The scheduler's goroutine fan-out — the one place Go's concurrency pays a real dividend — is absent here; independent cells run serially. It's invisible on these demos and would matter on a compute-heavy notebook. Native builds (your laptop, an HPC node) do fan out across cores.
The demos above are the notebook compiled to WebAssembly. Compile it for your cluster instead and it's a single static binary — no Python environment to reconstitute, no kernel, no spawner. The artifact that produced the figure is the artifact you rerun in six months.
$ go tool notebook build ./capacity # one static binary, no cgo $ scp capacity hpc:~ && ssh hpc $ sbatch ./capacity --headless \ --set servers=120 --set lambda=1400 --json {"cost": 120.72, "meets": true, "wq": 4.1, ...}
Interactive on your laptop, a slider in the browser, and a batch job on the cluster — the same file, distinguished only by where you put the compiler. That last part is the thing no interpreter-based notebook can say.