Skip to content

Playground: go-ndarray compiled to WebAssembly, step by step - #23

Merged
tannevaled merged 6 commits into
mainfrom
playground
Oct 8, 2026
Merged

tannevaled merged 6 commits into
mainfrom
playground

Conversation

@tannevaled

Copy link
Copy Markdown
Contributor

What

A playground at /playground/: go-ndarray itself, compiled to WebAssembly, inside a go-widgets canvas app (the go-tex playground's structure: a nested Go module under playground/, a Hugo layout, the wasm built at deploy). The landing's nav gets a Playground entry; the page reuses the landing's nav bar, styles.html and System/Light/Dark toggle unchanged (the icons from #21 are kept — this branch is rebased on #22).

A visitor can create arrays (Arange, Linspace, Zeros/Ones/Full, Eye, typed-in FromData) and apply, step by step, Reshape (with -1), Transpose, NumPy-style Slice (1:3, ::2, 0, -1), broadcasting Add/Sub/Mul/Div (a step or a number), Maximum/Minimum, ufuncs (Exp, Log, Sqrt, Sin, Cos, Abs, Square, Power, Clip), reductions whole or along an axis with keepdims (Sum, Mean, Max, Min, ArgMax, CumSum), Concatenate/Stack, MatMul/Dot/Outer. For each step: shape, strides in elements and bytes, offset, contiguity, whether it is a view and which earlier steps it shares memory with, the mean time per call in the browser, values as a viridis heatmap table, the Go statement and the NumPy one. The whole pipeline is shown as a Go program that compiles, or its NumPy equivalent, with a Copy button. Errors are go-ndarray's own text. Eight presets (README example, views, broadcasting, reductions, ufuncs, joins, linalg, a shape mismatch to try).

Architecture

  • playground/engine — model: op catalog, argument parsing, timed runs, Go/NumPy spellings, layout read from go-ndarray's Array (strides/offset/storage are unexported in v0.7.1, read via reflect, read-only; TestLayoutReadsTheLibrary fails if a release renames them). Size guard (2^22 elements) so a typo cannot OOM the tab.
  • playground/vm — ViewModel: all state as mvvm Observables/ObservableLists/Commands, no widget.
  • playground — View: toolkit Label, DropDown, ListBox, Entry, Button, ViewSwitcher, TextView, Table, VBox/HBox, bound with mvvmtk + view_binding.go. Nothing drawn by hand.
  • playground/cmd/playground-wasm — browser shell (device-pixel canvas, input, ndarraySetTheme, ndarrayDebug/ndarrayRects for the proofs).

Depends on go-widgets/toolkit#483 (Table.CellFill + Viridis + ContrastInk, and an Entry caret fix the bindings exposed). go.mod pins that PR's commit as a pseudo-version; once it is merged and tagged, bump to the tag.

go-ndarray's helper goroutines under wasm

Under GOOS=js GOMAXPROCS is 1, so every operation takes the serial path and no helper goroutine is ever started. Checked three ways: the engine suite run as wasm under node (goroutine count unchanged after 10^6-element Exp/Sum and a 1000² MatMul), the same test natively with GOMAXPROCS=4 as positive control (2 → 5 goroutines), and in Chrome by the browser proof. Timings on the page are therefore single-threaded and without SIMD.

Size

playground.wasm 13.0 MB, 3.4 MB gzipped (-s -w -trimpath). Not committed: hugo.yml builds it and copies wasm_exec.js from the same toolchain before Hugo, cache-busted with ?v=<sha>.

Gates

  • playground.yml (new): gofmt, vet (native + js/wasm), -race, 100% statement coverage of engine, vm and the View, the engine suite as wasm under node, wasm build + size in the summary; mvvmlint + bricolint with a negative control each (hack/lint-negative-controls.sh, both bite); a browser proof in headless Chrome.
  • ci.yml: also checks the playground page is built and the landing links to it.

Verification (run locally, own protocol)

TestBrowserProof builds the site exactly as the deploy does and drives it with real mouse/keyboard in headless Chrome, scheme forced, light@1x, dark@1x, light@2x — 114 assertions, all green: opens on the README example; canvas background = theme background (pixel); heatmap colours on screen (pixels); clicking a step selects it and makes it the input; picking Transpose in the popover + Apply adds a non-contiguous view; a bad reshape typed + Enter shows ndarray: shape mismatch… in red (pixels); NumPy tab; Copy puts the program on the clipboard; the mismatch preset fails with ndarray: shapes are not broadcastable: [2 3] vs [3 2]; GOMAXPROCS 1 and no new goroutine after 10^6-element steps; the nav toggle repaints the canvas light then dark (pixels); no console error. Negative control: with SetTheme disabled the proof fails on "toggle to Dark paints the dark background". Screenshots go outside every repository (the capture helper refuses a git work tree).

🤖 Generated with Claude Code

tannevaled and others added 6 commits October 8, 2026 19:11
…pp (WIP)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…nput, text at logical size

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…I, deploy wiring, README

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… over the tested packages

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…k read it as a URL)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… Entry caret clamp) instead of the PR pseudo-version

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@tannevaled
tannevaled merged commit 52d5cdb into main Oct 8, 2026
7 checks passed
@tannevaled
tannevaled deleted the playground branch October 8, 2026 17:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant