--- license: mit library_name: onnx tags: - audio-to-audio - audio-source-separation - onnx - onnxruntime-web - webgpu - demucs --- # htdemucs — ONNX graphs for onnxruntime-web (WebGPU) Hybrid Transformer Demucs, exported to ONNX and prepared for **in-browser** inference with [onnxruntime-web](https://onnxruntime.ai/) on the **WebGPU** backend. Used by [stemstudio.jp](https://www.stemstudio.jp) for local, upload-free stem separation. Derived from [facebookresearch/demucs](https://github.com/facebookresearch/demucs) (MIT). See `LICENSE` — the upstream notice travels with these files. ## Files | File | Transfer (gzip) | Inflated | Stems | |---|---|---|---| | `htdemucs_ft.opt.fp16.istft.onnx.gzbin` | 141,591,461 | 172,746,888 | vocals / instrumental (fine-tuned) | | `htdemucs_4s.opt.fp16.istft.onnx.gzbin` | 141,601,329 | 172,746,888 | vocals / drums / bass / other | | `htdemucs_6s.opt.fp16.istft.onnx.gzbin` | 114,203,693 | 142,573,723 | + guitar / piano | ## Two things that are easy to get wrong **1. The `.gzbin` extension is deliberate.** These are raw gzip streams stored as ordinary files, and the browser inflates them with `DecompressionStream('gzip')`. They are *not* named `.gz`, because some hosts and dev servers see that extension and add `content-encoding: gzip`, at which point the transport decompresses them and the application decompresses them again — which fails with `incorrect header check`. Detect gzip by the leading magic bytes (`1f 8b`), not by header or extension. The compression is in the file rather than in the transport because model hosts generally serve `.onnx` uncompressed regardless of `Accept-Encoding`. **2. Build order matters: optimize *before* fp16, not after.** Running the graph optimizer on an fp16 graph overflows folded constants to `inf` and every output becomes `NaN`. Worse, `nan > tolerance` is false, so a naive comparison reports "max diff 0.000e+00 / OK" on a completely broken model. The order used here is: fp32 export → graph optimization → fp16 weights → inverse STFT folded into the graph. ## Why the optimized graph is WebGPU-only Measured on one 7.8 s segment, same model, same host: | | wasm heap | wasm infer | WebGPU session build | |---|---|---|---| | unoptimized | 1,223 MB | 16.8 s | 15.6 s | | optimized | **3,251 MB** | 18.1 s | **1.4 s** | On WebGPU, session build gets ~11× faster. On wasm, heap grows 2.7× and inference gets *slower*. "Ship the optimized one and everyone gets faster" is false — pick per execution provider. ## Training data The upstream weights were trained on MUSDB18-HQ and additional sources. The MIT license covers the code and these derived graphs; the dataset carries its own terms, which are worth checking for your own use.