# wai-web demo — interactive-worlds browser sink `world.html` is a conformant `wai.world.replay` sink running in a tab: the WWLD payload (a tick-0 world + signed op log, ~1.3 KB for the truss vector) is re-derived frame by frame in WebGPU by the **same `wai` crate native sinks run**, compiled to wasm32 with the codec chain off (`--no-default-features --features world`). At the sealed final tick the sink recomputes the BLAKE3 state hash and compares it to the publisher's Ed25519-signed seal — replay-equivalence, demonstrated: the hash the browser computes is byte-identical to `world-conformance/expected_hashes.json`. ## Run ``` # from wai-web/: rebuild the wasm pkg after engine changes wasm-pack build --target web --out-dir demo/pkg # vectors come from the conformance corpus cp ../world-conformance/vectors/{truss_fracture,ballistic_bounce,basic_kinematics}.wwld demo/vectors/ # serve (any static server; WASM needs http, not file://) python3 -m http.server 8741 # → http://localhost:8741/demo/world.html ``` Needs a browser with WebGPU enabled. No build steps beyond wasm-pack; no JS dependencies. ## `int_synth.html` — integer-exact neural image sink (no ML runtime) `int_synth.html` decodes a `wai.neural.int_synth` payload to RGB with the **same pure-i64 Rust** that decodes natively, compiled to wasm32 — **no onnxruntime-web, no WebGPU, no float**. Because the decode is integer all the way down (the integer-entropy contract, SPEC §6), the reconstruction is byte-identical on every machine, so the page re-checks its BLAKE3 against the hash the native reference froze into `neural-conformance/expected_hashes.json`. The four staged vectors (`demo/int_synth/`, copied from the corpus) include the real *trained* `photo` model (128×128, 21.6 dB PSNR). ``` # from wai-web/: build the web pkg, then serve wasm-pack build --release --target web --out-dir demo/pkg --out-name wai_web python3 -m http.server 8741 # → http://localhost:8741/demo/int_synth.html ``` Reproducible CLI proof that the wasm decode equals the native corpus (builds the nodejs-target pkg and checks all four vectors' hashes): ``` ./demo/verify_int_synth.sh ``` ## Staged delivery — `StageSink` in the browser `src/stage.rs` binds `wai::staged::StageSink`, the staged-delivery sink a native player runs (`extensions/staged-delivery.md`), for JS: `StageBase.decode` decodes a base envelope by SPEC §4 through the integer path, with its pin and under the page's cost limit; `StageBase.fromStage0` takes a base the page decoded elsewhere, which is *claimed* (its chain's reports, units and stage hashes say so). `StageSink` adds bases (taking them), offers refinement layers, expires holds and removes bases in any order, and every unit's stage, producer, tier, output and stage hash reads back. `stageLayerInfo` reads a layer's `refines`, `WIR1` header and declared cost, and a base's declared decode cost, before either is offered. Reports and units come back in the corpus's own form (`staged-conformance/README.md`). `verify_staged_wasm.mjs` replays every case of `staged-conformance/` through the wasm32 build and requires every report, final unit, stage hash and clip hash the native sink wrote; each integer base is decoded in wasm32 and must give its `stage0.bin`. It then checks the binding's guards: bases that declare huge clips (`wai-rs/tests/vectors/staged_bounds/`) refused under a cost limit with linear memory watched, a base past the chain budget, base facts a page cannot raise, and a seeded fuzz that must not trap: ``` # from wai-web/ cargo build --release --target wasm32-unknown-unknown wasm-bindgen --target nodejs --out-dir demo/pkg-node --out-name wai_web \ target/wasm32-unknown-unknown/release/wai_web.wasm node demo/verify_staged_wasm.mjs ./pkg-node/wai_web.js ../staged-conformance ``` ## Staged measurement — the native figures in the browser `src/measure.rs` binds `wai::stage_measure`, the code the native measurement tool `wai_stage_measure` runs (`extensions/staged-measurement.md`), for JS. A title is read from a `Map` of files by their path below `wai/`: `titleMissing(dir, files)` names the next file it needs, so a page fetches files one at a time until it holds them all. `evaluateTitle` (or `new MeasureTitle(dir, files).json()`) gives the text `wai_stage_measure eval --json` prints: counted bytes per object, stage and prefix, quality integers per presentation and per prefix, first presentable bytes, the layering overhead's points, deduplication and the output tree. `simulate` and `simulateModel` give the text `wai_stage_measure sim` prints. `countObject` counts one envelope (or names the code it is not countable with), `qualityRgb8` gives one picture pair's integers (sums of squares as decimal strings), and `outputRoot` the output tree's root over presentations. `sessionBegin(entry, host, nonce)` is `wai::session_recorder::SessionRecorder` over the session's entry object: `decoderFor` names the decoder that decoded an envelope, `presented` and `received` record what the page did, and `finish(seed, signer_id, issued_at)` seals the `reconstruct-session` claim (`extensions/jwp-receipts.md` §2.4.12), checked against the record first; a browser session states no energy figure. `verifyClaim(claim, outputs, objects)` checks a claim against the presentations and the objects received, and gives `""` or the reason. `verify_measure.sh` builds the nodejs package (or takes one already built), runs `verify_measure_wasm.mjs` over `staged-measure-conformance/` — every golden, refusal title, counted object, quality pair and simulator case, byte for byte, and a session per fixture recorded the way a page records one, which must be the native tool's claim byte for byte — then requires the native `wai_stage_measure eval --json` to equal the wasm32 evaluation of every fixture, and the native and independent Python claim verifiers to give the wasm32 build's verdicts on every session it sealed: ``` # from wai-web/ (claim_verify.py needs the `cryptography` package) bash demo/verify_measure.sh ``` ## `quantum_webgpu.html` — the fabric's WebGPU member in a page `src/quantum_webgpu.rs` (feature `quantum_webgpu`, its own bundle) binds `wai::quantum_fabric_webgpu` for JS: `quantum_webgpu_discover()` opens the adapter the page is offered as a fabric member and probes its arithmetic against the CPU's, and the member it returns certifies the gate kernel against the CPU reference on a corpus (`certify`) and runs a `WQC` container to its statevector hash (`statevector_hash_hex`). The kernel is the same WGSL text the native member runs, compiled by the page's own WebGPU. A bit-exact member reproduces the CPU reference's pinned corpus digests, `quantum-conformance/fabric/expected.json`. The page runs `quantum_webgpu_worker.js` in a module worker: the four pinned certificates (both gatesets, random-v1 and fabric-v1) and every vector of the quantum corpus, on the member and again on the same adapter running the tile implementation (`tiled()`: runs of gates applied tile by tile, one dispatch per run, whose barrier the page's WGSL compiler holds to the uniformity rules); then every reject file (each refused with its listed class), and a circuit one qubit wider than the member holds. A second worker repeats the last check with the adapter offering only the WebGPU default buffer limits, where the member holds 25 qubits and refuses 26 as too wide. ``` # from wai-web/: the bundle has its own --out-name, so its imports never meet the main bundle's cargo build --release --target wasm32-unknown-unknown --features quantum_webgpu wasm-bindgen --target web --out-dir demo/pkg-webgpu --out-name wai_quantum_webgpu \ target/wasm32-unknown-unknown/release/wai_web.wasm # serve wai/: the page reads ../../quantum-conformance/ cd .. && python3 -m http.server 8741 # → http://localhost:8741/wai-web/demo/quantum_webgpu.html ``` `quantum-conformance/browser_webgpu.mjs` drives the page headless and checks every result against the committed corpus.