1# hitandrun-commonview
3One interactive figure, one shared view. This app shows the
4[hitandrun-interactive](https://github.com/concept-collection/hitandrun-interactive)
5hit-and-run sampling figure — just the figure, no source code — and keeps it
6**identical for everyone who has the page open**, in the style of
7[commonview](https://github.com/concept-collection/commonview): peers discover
8each other over nostr relays and form a WebRTC full mesh; the oldest peer is
9the **central** peer and owns the authoritative state.
11The twist over commonview's counter: the shared state is a live MATLAB
12computation. The central peer — and only the central peer — runs
13[numbl](https://numbl.org) (a MATLAB-compatible runtime) in a web worker,
14executing the *unmodified* `hitandrun_demo.m` from hitandrun-interactive. The
15figure's uihtml bridge is intercepted host-side (no iframe): control changes
16from **any** viewer are forwarded to the central peer, which feeds them to the
17script and broadcasts the results — parameter selections, the region, and the
18samples — to every viewer.
20## What is shared
22- **Parameters**: sample count, convex/non-convex region, local-segment mode.
23- **The region and the samples**: samples travel as a Float32 blob (up to
24 100,000 points ≈ 800 KB), streamed in 64 KB chunks over the data channel and
25 authenticated by a SHA-256 in a signed header. The JSON "view" message that
26 follows it references the blob by id.
27- **The sampling movie**: the central peer drives the animation clock, so every
28 viewer watches the same step at the same time.
30## Graceful failover
32- Every message is a signed envelope (schnorr over the peer's key, which *is*
33 its ID); state is only trusted from the current central peer.
34- Every peer keeps the latest sample blob, so whichever peer becomes central
35 can serve it to late joiners.
36- If the **central peer leaves**, the next-oldest peer becomes central, boots
37 its own engine (viewers see "starting engine…"), and continues from the
38 last-known state — the script is stateless (the region rides along with each
39 resample request), so any peer's engine can pick up where the last one left
40 off.
41- If the central peer's **engine fails to boot or to compute** (a timeout
42 counts), it announces the failure and steps down; the election skips
43 engine-failed peers, and the next-oldest healthy peer takes over.
44- State is never persisted: once all peers leave, the room resets.
46## Run
48```
49npm install # requires numbl >= 0.4.8 on npm (browser-embedding exports)
50npm run dev
51```
53Open the printed URL in **two different browsers or profiles** (two tabs in the
54same profile share the same localStorage key, so they'd be the *same* peer).
55Drag the samples slider or press "New region" in either window and watch both
56update; close the central window and watch the other take over.
58## How the engine embedding works
60`src/engine/numbl.worker.ts` runs `executeCode` from the `numbl` npm package
61against an in-memory filesystem holding the `.m` files (verbatim copies from
62hitandrun-interactive, under [matlab/](matlab/)). The script's
63`uihtml(...)` call surfaces as a plot instruction carrying the component id and
64initial Data; `sendEventToHTMLSource` calls surface via the `onHtmlSourceEvent`
65hook; and events from the app re-enter the still-live interpreter through the
66`UihtmlSession` returned by `executeCode` — firing the script's
67`HTMLEventReceivedFcn` exactly as if the figure page had sent them. The HTML
68the script loads for the figure is replaced by a one-line placeholder; nothing
69is ever rendered from the worker.