// WAI integer-exact CONDITIONAL codec sink — decode a wai.neural.int_hyper // bitstream (factorized z + Gaussian-conditional y) to RGB with the SAME pure-i64 // Rust that decodes natively, compiled to wasm32, with NO ML runtime (no // onnxruntime-web, no WebGPU, no float). Then verify the decoded image's FNV-1a // against the native reference hash, in-page — the integer-entropy contract: a // pretrained learned codec (mbt2018_mean, exported to integers) whose // reconstruction hash is portable. // // The model files (g_s/h_s, PTQ weights) are large and NOT committed; place the // export tool's output under demo/int_hyper/ (the .bin models are ~½ the JSON). import init, { decodeIntHyperBin } from './pkg/wai_web.js'; const $ = (id) => document.getElementById(id); const ui = { badge: $('badge'), dims: $('dims'), bits: $('bits'), psnr: $('psnr'), got: $('got'), want: $('want') }; const canvas = $('c'), g = canvas.getContext('2d'); const base = 'int_hyper'; // FNV-1a 64-bit — the same dependency-free checksum the native Rust decode and // the export tool share (raw RGB bytes, no domain separation). function fnv1a(buf) { const mask = (1n << 64n) - 1n; let h = 14695981039346656037n; for (let i = 0; i < buf.length; i++) h = ((h ^ BigInt(buf[i])) * 1099511628211n) & mask; return h.toString(16).padStart(16, '0'); } async function bytes(path) { const r = await fetch(path); if (!r.ok) throw new Error(`${path}: ${r.status}`); return new Uint8Array(await r.arrayBuffer()); } (async () => { await init(); let conf, gS, hS, ent, bs; try { conf = await (await fetch(`${base}/cond.json`)).json(); [gS, hS, ent, bs] = await Promise.all([ bytes(`${base}/int_synth_sota_model.bin`), // g_s (compact binary) bytes(`${base}/int_hs_model.bin`), // h_s (compact binary) bytes(`${base}/cond_entropy.json`), // y Gaussian-conditional + z factorized tables bytes(`${base}/cond_bitstream.json`), // z_rans + y_rans ]); } catch (e) { ui.badge.className = 'bad'; ui.badge.textContent = `model not staged — run tools/wai_sota_int_synth_export.py into demo/${base}/ (${e.message})`; return; } ui.badge.textContent = 'decoding (pure-i64 wasm, no ML runtime)…'; await new Promise((r) => requestAnimationFrame(r)); const t0 = performance.now(); const img = decodeIntHyperBin(gS, hS, ent, bs); // THE decode — pure-i64 Rust in wasm const ms = Math.round(performance.now() - t0); const rgb = img.rgb; const got = fnv1a(rgb); const ok = got === conf.rgb_hash && img.width === conf.out_w && img.height === conf.out_h; // Paint RGB → RGBA, nearest-neighbour upscaled to fill the canvas. const id = g.createImageData(img.width, img.height); for (let i = 0, j = 0; i < rgb.length; i += 3, j += 4) { id.data[j] = rgb[i]; id.data[j + 1] = rgb[i + 1]; id.data[j + 2] = rgb[i + 2]; id.data[j + 3] = 255; } const off = new OffscreenCanvas(img.width, img.height); off.getContext('2d').putImageData(id, 0, 0); g.imageSmoothingEnabled = false; g.clearRect(0, 0, canvas.width, canvas.height); const scale = Math.min(canvas.width / img.width, canvas.height / img.height); const dw = img.width * scale, dh = img.height * scale; g.drawImage(off, (canvas.width - dw) / 2, (canvas.height - dh) / 2, dw, dh); const bsj = JSON.parse(new TextDecoder().decode(bs)); const nbytes = bsj.z_rans.length + bsj.rans.length; ui.dims.textContent = `${img.width}×${img.height}`; ui.bits.textContent = `${nbytes} B (${ms} ms decode)`; ui.psnr.textContent = conf.psnr_cond ? `${conf.psnr_cond.toFixed(2)} dB` : '—'; ui.got.textContent = got; ui.want.textContent = conf.rgb_hash; ui.badge.className = ok ? 'ok' : 'bad'; ui.badge.textContent = ok ? '✓ hash matches the native reference' : '✗ HASH MISMATCH'; })();