Fork me on GitHub

Go notebooks, compiled and reactive.
Native speed, kept values, a reproducible order.

A notebook is a Markdown file whose ```go blocks are cells. The kernel compiles it into a real Go program, orders the cells by what they use โ€” worked out from the code itself, as marimo does for Python, not from the order you ran them in โ€” and runs only what changed. A cell whose code and inputs did not change keeps its result, and its values are handed over instead of being computed again, across rebuilds and across runs.

6 cell rules, checked from the syntax.md diffs are diffs of text100% test coverage
spectrum.md
# Spectrum

```go
import "math"

n := 1_000_000
```

The sum below runs once; editing the last cell does not run it again.

```go
sum := 0.0
for i := 1; i <= n; i++ {
	sum += 1 / float64(i*i)
}
```

```go
math.Sqrt(6 * sum)
```
4 repositories โ€” one command: notebook run, html, watch, serve
Ask it, do not take our word

The notebook above, actually run โ€” twice.

The first run compiles the notebook and runs every cell. Then the last cell is changed to math.Pi - math.Sqrt(6*sum) and the notebook is run again, as a new process: the first two cells are kept, and sum comes back from the kernel's store instead of being summed again. The output below is a real run, with nothing edited.

notebook run spectrum.md โ€” zsh
$ notebook run spectrum.md
โ”€โ”€ cell 0 (line 4, ran in 12ยตs)
โ”€โ”€ cell 1 (line 12, ran in 2.663ms)
โ”€โ”€ cell 2 (line 19, ran in 36ยตs)
   3.1415916986605086
# change the last cell to math.Pi - math.Sqrt(6*sum), then:
$ notebook run spectrum.md
โ”€โ”€ cell 0 (line 4, kept)
โ”€โ”€ cell 1 (line 12, kept)
โ”€โ”€ cell 2 (line 19, ran in 42ยตs)
   9.54929284535666e-07
Why go-notebook

Speed, kept values and order โ€” together.

Jupyter runs Python in an interpreter, with hidden mutable state, in whatever order the cells were clicked. gophernotes interprets Go. GoNB compiles, but keeps only declarations: values are computed again on every run. marimo orders cells from the code, but interprets Python. None has native speed, kept values and a reproducible order together; go-notebook is built to have all three.

๐Ÿ“

A notebook is Markdown

Prose is kept byte for byte (format.Parse(src).String() == src, fuzzed). GitHub shows a notebook as a document, a diff is a diff of text, and no output is ever written into the file.

โš™๏ธ

Compiled, native speed

The kernel compiles the notebook into a worker process. Go's build cache makes a rebuild after a one-cell edit cost about a tenth of a second.

๐Ÿงญ

Order from the code

Each cell's definitions and free identifiers come from the parse alone, so the graph works on code that does not compile yet. Cycles, double definitions and foreign assignments are errors.

โ™ป๏ธ

Kept values

A cell's key hashes its source, the packages it refers to and the keys of the cells it reads. An unchanged key keeps its result; after a rebuild its variables are handed to the new worker. Moving cells runs nothing.

๐Ÿ’พ

On disk, across runs

Results and values are kept in the work directory, so a new kernel, or the next notebook run, starts where the last one stopped.

๐Ÿ“„

A page with no scripts

notebook html writes a standalone page: no scripts, no external resources, raw HTML in the prose left out. A page made from someone else's notebook runs nothing of theirs.

โœ…

100% test coverage

Gated in CI on Linux, macOS and Windows.

โš–๏ธ

BSD-3-Clause

Every repository of the organization.

The cell rules

6 rules make the order a function of the code.

Go, with statements at the top level

x := load(path) or fmt.Println(x) at the top level of a cell is most of what a notebook is. Top-level func, type and import are declarations of the notebook; the rest runs in order.

One definition per name

A name defined at the top level (:=, var, const, func, type) is shared with every cell, and exactly one cell defines it. Go already forbids two package-level declarations of a name.

No assignment to another cell's variable

The dependency graph would not see the change, so it is refused. Mutation through a method or an element cannot be seen statically and is not refused, as in marimo.

Private names stay in their cell

Names starting with _, and err, which nearly every cell declares and none means to share.

The final expression is shown

A value with a Display() map[string][]byte method shows itself as HTML, SVG or PNG; anything else shows its %v.

Errors name the notebook's lines

//line directives point every generated line back into the .md. A panic stops the cells that read the failing one, and they say why.

Repositories

What is here.

notebook

kernel ยท command

The kernel, the notebook command (run, html, watch, serve) and the HTML export: format parses the Markdown, analysis builds the dependency graph from the syntax, gen writes the Go program, kernel compiles and drives it through runtime and keeps results and values, export writes the page.

playground

wasm ยท page

A notebook in the browser, with no server. The page shows the prose as text and each cell as an editor; Ctrl+Enter runs the whole notebook again in the tab. A Go interpreter (yaegi) compiled to WebAssembly runs the cells, while the libraries they import โ€” go-statistics, go-numerics, go-ndarray, go-plot โ€” run compiled: only the notebook's own code is interpreted. Each run posts the whole notebook to a Web Worker and gets every cell's results back, one call in and one string out, so a long run neither freezes the page nor costs a call per widget. Nothing reaches the network or a file system. Deployed as /playground/ on go-statistics and go-numerics.

docs

site

The documentation (Hugo + Hextra): what a notebook is, the cell rules, the command, how the kernel decides what to run, and why it is built this way. Source โ†’

brand

assets

Logos and icons for the organization, in SVG / PNG / JPG / ICO / ICNS across colour, white and black variants.

Notebooks for the Go scientific stack.

go-notebook is part of an ecosystem with go-ndarray (NumPy-style arrays) and go-fft (numpy.fft). What is not done yet โ€” a Jupyter front end, concurrent branches, LaTeX in prose โ€” is listed in the documentation.

Read the source on GitHub โ†’