// WAI integer-exact neural image sink — decode a wai.neural.int_synth payload // 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 BLAKE3 against the frozen native conformance hash, in-page — // the integer-entropy contract: a learned-codec reconstruction whose hash is // portable, so it verifies client-side with no server round-trip. import init, { decodeIntSynth, blake3Hex } from './pkg/wai_web.js'; const $ = (id) => document.getElementById(id); const ui = { vec: $('vec'), badge: $('badge'), dims: $('dims'), inch: $('inch'), layers: $('layers'), got: $('got'), want: $('want'), }; const canvas = $('c'), g = canvas.getContext('2d'); // The conformance hash is domain-separated: blake3("wai:int-synth-rgb"||0x01||rgb). const DOMAIN = new Uint8Array([...new TextEncoder().encode('wai:int-synth-rgb'), 0x01]); await init(); const expected = await (await fetch('int_synth/expected_hashes.json')).json(); async function bytes(path) { return new Uint8Array(await (await fetch(path)).arrayBuffer()); } async function load(name) { const exp = expected[name]; const payload = await bytes(`int_synth/${name}/latent.bin`); const spec = await bytes(`int_synth/${name}/int_synth.json`); // THE decode — pure-i64 Rust in wasm, no ML runtime. const img = decodeIntSynth(payload, spec); const rgb = img.rgb; // width*height*3 // In-page receipt check: domain-separated BLAKE3, compared to the frozen hash // the native reference froze into expected_hashes.json. const buf = new Uint8Array(DOMAIN.length + rgb.length); buf.set(DOMAIN, 0); buf.set(rgb, DOMAIN.length); const got = blake3Hex(buf); const ok = got === exp.rgb_hash && img.width === exp.width && img.height === exp.height; // Paint RGB → RGBA on the canvas, nearest-neighbour upscaled to fill. 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); ui.dims.textContent = `${img.width}×${img.height}`; ui.inch.textContent = exp.in_channels; ui.layers.textContent = exp.layers; ui.got.textContent = got.slice(0, 16) + '…'; ui.want.textContent = exp.rgb_hash.slice(0, 16) + '…'; ui.badge.className = ok ? 'ok' : 'bad'; ui.badge.textContent = ok ? '✓ hash matches native conformance' : '✗ HASH MISMATCH'; } ui.vec.addEventListener('change', () => load(ui.vec.value)); load(ui.vec.value);