File size: 2,775 Bytes
791f996 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | ---
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.
|