Skip to content

Instantly share code, notes, and snippets.

@JessicaWachtel
Created January 19, 2026 18:02
Show Gist options
  • Select an option

  • Save JessicaWachtel/b92783215d7ae5900b508799d893c458 to your computer and use it in GitHub Desktop.

Select an option

Save JessicaWachtel/b92783215d7ae5900b508799d893c458 to your computer and use it in GitHub Desktop.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Wasm vs JS: The Showdown</title>
<style>
body { font-family: sans-serif; text-align: center; background: #f4f4f4; padding: 20px; }
.race-box { background: white; padding: 20px; border-radius: 10px; margin: 20px auto; max-width: 800px; border: 1px solid #ddd; }
.container { display: flex; justify-content: center; gap: 20px; }
canvas { background: #eee; max-width: 45%; height: auto; border: 1px solid #999; }
.stats { font-weight: bold; padding: 10px; border-radius: 5px; margin-bottom: 10px; }
.js-label { color: #d68910; }
.wasm-label { color: #2e86c1; }
button { padding: 10px 20px; cursor: pointer; font-weight: bold; }
</style>
</head>
<body>
<h1>How Much Faster is WebAssembly?</h1>
<input type="file" id="upload" accept="image/*"><br><br>
<div class="race-box">
<h2>Race 1: Simple Grayscale (Light Math)</h2>
<button id="btnGrayscale">Run Grayscale Race</button>
<div class="container">
<div>
<div id="jsTimeGray" class="stats js-label">JS: --ms</div>
<canvas id="canvasJSGray"></canvas>
</div>
<div>
<div id="wasmTimeGray" class="stats wasm-label">Wasm: --ms</div>
<canvas id="canvasWasmGray"></canvas>
</div>
</div>
</div>
<div class="race-box">
<h2>Race 2: Sharpen Filter (Heavy Task)</h2>
<button id="btnSharpen">Run Sharpen Race</button>
<div class="container">
<div>
<div id="jsTimeSharp" class="stats js-label">JS: --ms</div>
<canvas id="canvasJSSharp"></canvas>
</div>
<div>
<div id="wasmTimeSharp" class="stats wasm-label">Wasm: --ms</div>
<canvas id="canvasWasmSharp"></canvas>
</div>
</div>
</div>
<script type="module">
import init, { apply_grayscale_wasm, apply_sharpen_wasm } from './pkg/wasm_image_proc.js';
async function start() {
await init();
const upload = document.getElementById('upload');
const canvases = ['canvasJSGray', 'canvasWasmGray', 'canvasJSSharp', 'canvasWasmSharp'].map(id => document.getElementById(id));
const ctxs = canvases.map(c => c.getContext('2d'));
upload.onchange = (e) => {
const img = new Image();
img.onload = () => {
canvases.forEach(c => { c.width = img.width; c.height = img.height; });
ctxs.forEach(ctx => ctx.drawImage(img, 0, 0));
};
img.src = URL.createObjectURL(e.target.files[0]);
};
// RACE 1: Light Task
document.getElementById('btnGrayscale').onclick = () => {
const w = canvases[0].width; const h = canvases[0].height;
// JS Grayscale
const imgJS = ctxs[0].getImageData(0,0,w,h);
const t0 = performance.now();
for(let i=0; i<imgJS.data.length; i+=4) {
const avg = (imgJS.data[i] + imgJS.data[i+1] + imgJS.data[i+2])/3;
imgJS.data[i] = imgJS.data[i+1] = imgJS.data[i+2] = avg;
}
document.getElementById('jsTimeGray').innerText = `JS: ${(performance.now()-t0).toFixed(2)}ms`;
ctxs[0].putImageData(imgJS, 0, 0);
// Wasm Grayscale
const imgWasm = ctxs[1].getImageData(0,0,w,h);
const t1 = performance.now();
apply_grayscale_wasm(imgWasm.data);
document.getElementById('wasmTimeGray').innerText = `Wasm: ${(performance.now()-t1).toFixed(2)}ms`;
ctxs[1].putImageData(imgWasm, 0, 0);
};
// RACE 2: Heavy Task
document.getElementById('btnSharpen').onclick = () => {
const w = canvases[2].width; const h = canvases[2].height;
// JS Sharpen
const imgJS = ctxs[2].getImageData(0,0,w,h);
const copy = new Uint8Array(imgJS.data);
const t0 = performance.now();
for (let y = 1; y < h - 1; y++) {
for (let x = 1; x < w - 1; x++) {
const i = (y * w + x) * 4;
for (let c = 0; c < 3; c++) {
const center = i + c;
const val = 5 * copy[center] - copy[center - (w*4)] - copy[center + (w*4)] - copy[center - 4] - copy[center + 4];
imgJS.data[center] = Math.min(Math.max(val, 0), 255);
}
}
}
document.getElementById('jsTimeSharp').innerText = `JS: ${(performance.now()-t0).toFixed(2)}ms`;
ctxs[2].putImageData(imgJS, 0, 0);
// Wasm Sharpen
const imgWasm = ctxs[3].getImageData(0,0,w,h);
const t1 = performance.now();
apply_sharpen_wasm(imgWasm.data, w, h);
document.getElementById('wasmTimeSharp').innerText = `Wasm: ${(performance.now()-t1).toFixed(2)}ms`;
ctxs[3].putImageData(imgWasm, 0, 0);
};
}
start();
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment