/ concept-collection / hitandrun-commonview
Sign in
concept-collection / hitandrun-commonview
hitandrun-commonview
Go to fileHistoryFork
.githubInitial commit: shared-view hit-and-run sampler
matlabInitial commit: shared-view hit-and-run sampler
srcInitial commit: shared-view hit-and-run sampler
.gitignoreInitial commit: shared-view hit-and-run sampler
CLAUDE.mdInitial commit: shared-view hit-and-run sampler
index.htmlInitial commit: shared-view hit-and-run sampler
package-lock.jsonInitial commit: shared-view hit-and-run sampler
package.jsonInitial commit: shared-view hit-and-run sampler
README.mdInitial commit: shared-view hit-and-run sampler
tsconfig.jsonInitial commit: shared-view hit-and-run sampler
vite.config.tsInitial commit: shared-view hit-and-run sampler

hitandrun-commonview#

One interactive figure, one shared view. This app shows the hitandrun-interactive hit-and-run sampling figure — just the figure, no source code — and keeps it identical for everyone who has the page open, in the style of commonview: peers discover each other over nostr relays and form a WebRTC full mesh; the oldest peer is the central peer and owns the authoritative state.

The twist over commonview's counter: the shared state is a live MATLAB computation. The central peer — and only the central peer — runs numbl (a MATLAB-compatible runtime) in a web worker, executing the unmodified hitandrun_demo.m from hitandrun-interactive. The figure's uihtml bridge is intercepted host-side (no iframe): control changes from any viewer are forwarded to the central peer, which feeds them to the script and broadcasts the results — parameter selections, the region, and the samples — to every viewer.

What is shared#

  • Parameters: sample count, convex/non-convex region, local-segment mode.
  • The region and the samples: samples travel as a Float32 blob (up to 100,000 points ≈ 800 KB), streamed in 64 KB chunks over the data channel and authenticated by a SHA-256 in a signed header. The JSON "view" message that follows it references the blob by id.
  • The sampling movie: the central peer drives the animation clock, so every viewer watches the same step at the same time.

Graceful failover#

  • Every message is a signed envelope (schnorr over the peer's key, which is its ID); state is only trusted from the current central peer.
  • Every peer keeps the latest sample blob, so whichever peer becomes central can serve it to late joiners.
  • If the central peer leaves, the next-oldest peer becomes central, boots its own engine (viewers see "starting engine…"), and continues from the last-known state — the script is stateless (the region rides along with each resample request), so any peer's engine can pick up where the last one left off.
  • If the central peer's engine fails to boot or to compute (a timeout counts), it announces the failure and steps down; the election skips engine-failed peers, and the next-oldest healthy peer takes over.
  • State is never persisted: once all peers leave, the room resets.

Run#

npm install        # requires numbl >= 0.4.8 on npm (browser-embedding exports)
npm run dev

Open the printed URL in two different browsers or profiles (two tabs in the same profile share the same localStorage key, so they'd be the same peer). Drag the samples slider or press "New region" in either window and watch both update; close the central window and watch the other take over.

How the engine embedding works#

src/engine/numbl.worker.ts runs executeCode from the numbl npm package against an in-memory filesystem holding the .m files (verbatim copies from hitandrun-interactive, under matlab/). The script's uihtml(...) call surfaces as a plot instruction carrying the component id and initial Data; sendEventToHTMLSource calls surface via the onHtmlSourceEvent hook; and events from the app re-enter the still-live interpreter through the UihtmlSession returned by executeCode — firing the script's HTMLEventReceivedFcn exactly as if the figure page had sent them. The HTML the script loads for the figure is replaced by a one-line placeholder; nothing is ever rendered from the worker.

moveopenescclose