AI images that form as you type.
Real-time diffusion in your browser. Nothing uploads. No login, no account, no credits.
Powered by fused WebGPU dispatches — every speedup on this page is measured live on
your GPU, and the PyTorch head-to-head ships in the repo (bench-torch.py).
No guidance control — SD-Turbo is distilled to run guidance-free, and CFG would double the U-Net cost per step.
Probe: element-wise chain (mul·add → relu → sub → tanh → mul → sigmoid) over 1M floats. Fused = 1 dispatch, chain lives in registers. Naive = 6 dispatches, intermediates in global memory. Both paths are diffed against each other at boot (max abs Δ reported below); the timing delta is pure dispatch + memory-traffic overhead.
Waiting for probe…
FFN block: (matmul → GELU → matmul → residual) at mid-block shape [2·1024·1280]·[1280·5120]·[5120·1280]. Naive = 4 dispatches with intermediates in global memory. Fused = 2 dispatches with GELU + residual baked into matmul epilogues. Same tiled matmul in both paths — the delta is fusion, not kernel quality.
Waiting for FFN block benchmark…
Self-attention at SD mid-block shape [B=2·H=20·S=256·head=64]. Naive = 3 dispatches (scores → softmax → attn-matmul) with scores + probs matrices materialized in global memory. Fused = 1 dispatch, scores stay in workgroup memory, softmax reduces cooperatively, output emits directly.
Waiting for attention benchmark…
One full SD-Turbo transformer block [B=2·S=256·D=1280·heads=20×64·D_FFN=5120]. Naive = 14 dispatches (LN → Q/K/V → scores → softmax → attn → out-proj → residual → LN → FFN-up → GELU → FFN-down → residual). Fused = 9 dispatches (flash-attn collapses 3 into 1, GELU folds into FFN-up matmul, both residuals fold into their producer matmul epilogues). Runs ~16× per U-Net forward pass.
Waiting for full-block benchmark…
GroupNorm at SD-Turbo mid-block feature-map shape [B=1·C=1280·G=32·16×16]. Naive = 2 dispatches (stats → normalize, with mean/rsigma materialized in scratch). Fused = 1 dispatch, one workgroup per (b,g), 256-thread parallel tree-reduce for mean and variance, stats kept in workgroup scalars. Runs ~30× per U-Net forward pass.
Waiting for GroupNorm benchmark…
Direct 3×3 Conv2d at SD-Turbo mid-block shape [B=1·C=1280·16×16·k=3]. Naive = 3 dispatches (conv → silu → residual-add), with Y re-read + re-written twice. Fused = 1 dispatch, SiLU + residual baked into the conv's per-output-value epilogue. Conv is called ~34× per U-Net forward pass.
Waiting for Conv2d benchmark…
Full SD-Turbo mid-block ResNet [B=1·C=1280·G=32·16×16·k=3]. Naive = 9 dispatches (GN-stats → GN-norm → SiLU → Conv1 → GN-stats → GN-norm → SiLU → Conv2 → skip-add). Fused = 4 dispatches (GN+SiLU collapsed via shared-mem stats, skip-add folded into Conv2's epilogue). First milestone where v2.5.0 and v2.5.1 chain end-to-end. Runs ~20× per U-Net forward pass.
Waiting for ResNet benchmark…
Cross-attn at SD-Turbo mid-block shape [B=2·H=20·S_q=256·S_kv=77·head=64]. Queries from the image feature map, K/V from text cond. Naive = 3 dispatches with scores [S_q × S_kv] in global memory. Fused = 1 dispatch, softmax kept on-chip in workgroup memory. Called ~32× per U-Net forward pass.
Waiting for cross-attention benchmark…
Timestep embed: sinusoidal [320] → Linear [1280] → SiLU → Linear [1280]. Fires once per denoise step. Naive = 3 dispatches. Fused = 1 dispatch with both intermediates (emb 320, hidden 1280) living in workgroup memory.
Waiting for timestep-embed benchmark…
End-to-end test of the hand-rolled ONNX protobuf parser against the real 1.73 GB SD-Turbo U-Net. Opt-in because it's a 1.73 GB download each run (this diagnostic path is deliberately not cached). Proves onnx-parser.js reads every TensorProto from the schmuell bundle without any protobuf library.
Click to fetch + parse the full U-Net and print the tensor-name tree. This is the audit step before v2.5.6 wires wgslUnetForward against real weights.
How it works
Every browser Stable Diffusion today (Web-SD, ONNX Runtime Web, Transformers.js) runs the denoising U-Net as dozens of kernel launches per step, times 20–50 steps. On consumer GPUs the launch overhead dominates — best-in-class browser SDXL Turbo is ~1 second on an RTX 4090. Nobody has broken the interactive barrier.
Our approach: collapse the sequential path.
The fused U-Net step is where the thesis lives: attention + GEMMs + GroupNorms + SiLU + residual adds collapsed into a single-digit number of dispatches per denoising step — vs. 60+ in ORT Web today. Diffusion is the textbook case where 1-dispatch fusion pays off, because the sequential step loop is launch-overhead-bound.
Roadmap
Shipped — fused kernels (v1.2). Hand-fused WGSL attention, FFN, and full transformer block at SD-Turbo shapes, benchmarked live above: 14 → 9 dispatches per block, correctness gated against the naive path. Plus fused GroupNorm, Conv, ResNet, cross-attention, and timestep embed as building blocks for our own U-Net.
Shipped — the product loop. ORT Web U-Net + our Euler scheduler generate real images on-device: as-you-type live preview, a continuous generate loop, and a camera mirror (img2img). Models persist via the Cache API after the first ~2.5 GB download.
In progress — our WGSL U-Net (v2.5). Replace ORT's graph execution with our own dispatch loop over the fused blocks: ONNX parsed byte-level, weights straight into our kernels. Target: interactive latency (<100 ms per preview refresh) on mid-range consumer GPUs.
Next — shareable.
URL-fragment-encoded prompt + seed + model + LoRA. Zero-backend sharing, same pattern
as the .flora adapter format.