Can this browser decode 16-channel Opus? Four native surfaces, one WebAssembly decoder, one pair of ears

Two Opus clips are embedded in this page and offered to every decode surface a web page has. The 16-channel clip carries a different sine on each channel (channel k = 200 + 100 k Hz, so channel 0 is 200 Hz and channel 15 is 1700 Hz), which lets the page verify not only that a decoder produced 16 channels but that they came out in the right order. The 2-channel clip (200 and 300 Hz) is the control. The same encode is carried in WebM and in fragmented MP4, and its raw packets are embedded a third time for the two surfaces that take packets rather than files. Nothing is fetched, so the page also runs from a local copy of index.html.

The first two rows are answers about a MIME string and cannot see a channel count; the next two actually decode. A browser that says yes to audio/webm; codecs="opus" and then refuses to decode the 16-channel WebM is not a contradiction in the page, it is the finding: the string surfaces promise what the decoders then refuse.

Expected on Safari (macOS and iOS): both decoding rows refuse the 16-channel clip in both containers, the WASM row decodes it with all 16 channels in order, and you hear both tones. Expected on Chrome and Firefox: everything passes.

running...
surface16-ch WebM16-ch fMP42-ch WebM2-ch fMP4
HTMLMediaElement.canPlayType
string answer; the MIME string carries no channel count, so both clips get the same one
...... ......
MediaSource.isTypeSupported and ManagedMediaSource.isTypeSupported
whichever exist; iOS ships only ManagedMediaSource, and the cell says which one answered
...... ......
AudioContext.decodeAudioData
actual decode of the file, then the per-channel tone check
...... ......
WebCodecs AudioDecoder
isConfigSupported, then an actual decode of the packets; the OpusHead is passed as description for 16 channels
... ...
WebAssembly libopus (opus-decoder 0.7.12)
the same packets, decoded in JavaScript; not a native surface
... ...

WASM decode, channel by channel

channelexpected Hzdetected Hz

Human verdict

Independent of everything above. Play sends the WASM-decoded channel 0 (200 Hz) for one second and then channel 15 (1700 Hz, a much higher pitch) for one second to both ears. A machine verdict can say OK while the speaker stays silent, so this row exists to catch that. iPhone: Play asks for the playback audio session category first, so the ring/silent switch does not mute the tones.

Play is enabled once the WASM row has decoded.
Raw results (JSON)