Makes the in-browser decode of the full conditional codec practical: the model
shrinks from ~52MB JSON to ~23.5MB binary (~15.5MB gzipped on the wire), and a
real HTML page decodes a SOTA-class learned codec in the browser, verifying its
hash against the native reference.
- typed IntModel: refactored run_net/decode/decode_conditional off serde_json
Value onto an IntModel { afrac, gf, shift, layers: Vec<IntLayer> }, built by
either IntModel::from_json (existing schema) or IntModel::from_bytes (compact
binary: magic WHM1, int12 weights as i16, IGDN gamma as i32, the rest i64).
Decode is byte-identical whichever the source.
- export tool writes *_model.bin alongside the JSON (model_to_bin).
- conformance: the binary model decodes byte-identically to JSON (new assert in
tests/int_hyper_synth.rs); all 3 tests green in release (~11s, the 100s was
debug — release wasm decode is ~10s).
- wasm: decodeIntHyperBin (binary path) beside decodeIntHyper (JSON); shared
cond_decode helper. demo/verify_int_hyper_wasm.mjs checks both paths.
- demo/int_hyper.html + .js: fetch the binary models + 1.4KB bitstream, decode
with pure-i64 wasm (no ML runtime), paint the canvas, FNV-1a the RGB against
cond.json. Verified in headless Chrome: 256x256, 42.61 dB, decoded hash
adeb12d347d0bf7e == native, ~10s decode, zero console errors.
The PTQ model files are large and NOT committed (gitignored demo/int_hyper/);
regenerate with tools/wai_sota_int_synth_export.py. pkg/ stays a build artifact.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
55 lines
3.6 KiB
HTML
55 lines
3.6 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>WAI — integer-exact conditional codec, in-browser</title>
|
||
<style>
|
||
:root { --bg:#06080c; --bg2:#0d1117; --border:#1e2736; --text:#e6edf5; --text2:#8b99ad; --text3:#4a5568; --g:#00e5a0; --w:#f0b429; --mono:'JetBrains Mono','SF Mono',Menlo,monospace; }
|
||
* { box-sizing:border-box; margin:0; padding:0; }
|
||
body { background:var(--bg); color:var(--text); font-family:var(--mono); padding:24px; }
|
||
h1 { font-size:15px; letter-spacing:.04em; margin-bottom:4px; } h1 em { font-style:normal; color:var(--g); }
|
||
.sub { font-size:11px; color:var(--text2); margin-bottom:14px; max-width:880px; line-height:1.5; }
|
||
code { color:var(--w); }
|
||
canvas { background:#04060a; border:1px solid var(--border); border-radius:10px; display:block; image-rendering:pixelated; }
|
||
.hud { display:flex; gap:22px; margin-top:10px; font-size:11px; color:var(--text2); flex-wrap:wrap; }
|
||
.hud b { color:var(--text); } .hash { color:var(--g); }
|
||
#badge { font-size:12px; padding:4px 12px; border-radius:99px; display:inline-block; background:var(--bg2); color:var(--text2); border:1px solid var(--border); margin-top:10px; }
|
||
#badge.ok { background:rgba(0,229,160,.12); color:var(--g); border-color:rgba(0,229,160,.5); }
|
||
#badge.bad { background:rgba(255,107,107,.12); color:#ff6b6b; border-color:rgba(255,107,107,.5); }
|
||
.note { margin-top:14px; font-size:10.5px; color:var(--text3); max-width:880px; line-height:1.55; }
|
||
.note b { color:var(--text2); }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<h1>WAI <em>integer-exact conditional codec</em> — in-browser, no ML runtime</h1>
|
||
<p class="sub">
|
||
A <code>wai.neural.int_hyper</code> bitstream arrived: a factorized side-stream <code>z</code> and the
|
||
Gaussian-conditional main stream <code>y</code>, both rANS-coded. This tab decodes it to RGB with the
|
||
<b>same pure-i64 Rust</b> that decodes natively, compiled to wasm32 — <b>no onnxruntime-web, no WebGPU,
|
||
no float anywhere</b>. The <i>whole</i> path is integer: factorized <code>z</code> → integer
|
||
hyper-decoder → integer-σ entropy bucketing → integer synthesis. Because of that (the integer-entropy
|
||
contract) the reconstruction is byte-identical on every machine, so the page re-checks its FNV-1a against
|
||
the hash the native reference produced — a SOTA-class learned codec verified client-side, no server.
|
||
</p>
|
||
<canvas id="c" width="512" height="512"></canvas>
|
||
<div id="badge">loading model…</div>
|
||
<div class="hud">
|
||
<span>image <b id="dims">—</b></span>
|
||
<span>bitstream <b id="bits">—</b></span>
|
||
<span>quality <b id="psnr">—</b></span>
|
||
<span>decoded hash <b class="hash" id="got">—</b></span>
|
||
<span>native hash <b class="hash" id="want">—</b></span>
|
||
</div>
|
||
<p class="note">
|
||
<b>Why this is the moat:</b> the SOTA learned-image family is float, so its entropy decode desyncs on
|
||
~1e-5 of cross-platform drift in the scale σ — reproducible only on the same build, never receiptable.
|
||
<code>int_hyper</code> is a real pretrained codec (post-training-quantized to integer, ~0.6 dB off
|
||
the float baseline) whose entire decode is integer, so the hash above is portable: this browser, a native
|
||
sink, and the conformance corpus all produce the same bytes. The wire carried a ~1.4 KB bitstream;
|
||
the model is the installed codec. <b>Note:</b> the model files are large (PTQ weights) and not committed —
|
||
generate them with <code>tools/wai_sota_int_synth_export.py</code> into <code>demo/int_hyper/</code>.
|
||
</p>
|
||
<script type="module" src="int_hyper.js"></script>
|
||
</body>
|
||
</html>
|