The Problem — HEVC Everywhere Except the Browser HEVC/H.265 is the standard codec for Netflix, Apple, broadcasters, 4K/HDR. It saves 30-50% bandwidth versus H.264 at equivalent quality — millions in annual CDN savings for streaming services. But browser support is a mess. macOS — Safari, Chrome, Edge, Firefox all decode HEVC natively via VideoToolbox. No extension needed. Chrome 107+ on Windows — uses D3D11VA directly. No Microsoft extension required, but needs a GPU with hardware HEVC decoder (Intel Skylake 2015+, NVIDIA Maxwell 2nd gen+, AMD Fiji+). No software fallback. Edge on Windows — uses Media Foundation. Requires the Microsoft HEVC Video Extension ($1 on the Store). Without it, no HEVC regardless of GPU. Firefox 133+ on Windows — same MFT path, same extension dependency. Linux — Chrome with VAAPI, maybe. Firefox, no. The root cause is licensing. MPEG LA and Access Advance impose per-unit royalties. Microsoft passes this to users via the Store extension. Google negotiated a direct D3D11VA path. Mozilla relies on Microsoft's extension. The result: publishers must either encode everything twice (H.264 + HEVC) or accept that some users get a black screen. The Solution — Decode HEVC Client-Side in WebAssembly What if the browser didn't need to know it's playing HEVC? hevc.js decodes HEVC in a Web Worker and re-encodes to H.264 via WebCodecs, delivering standard H.264 to Media Source Extensions. The player doesn't know it's happening. fMP4 HEVC → mp4box.js (demux) → NAL units → WASM H.265 decoder → YUV frames → WebCodecs VideoEncoder → H.264 → custom fMP4 muxer → MSE → The HEVC decoder is a from-scratch C++17 implementation of ITU-T H.265 (716 pages), compiled to WebAssembly. 236 KB gzipped. Zero dependencies. No special server headers needed. dash.js integration The plugin intercepts MediaSource.addSourceBuffer() . When dash.js creates an HEVC SourceBuffer, a proxy accepts the HEVC MIME type but feeds the real SourceBuffer with H.264. ABR, seek, live — everything works unmodified. import dashjs from ' dashjs ' ; import { attachHevcSupport } from ' @hevcjs/dashjs-plugin ' ; const player = dashjs . MediaPlayer (). create (); await attachHevcSupport ( player , { workerUrl : ' /transcode-worker.js ' , wasmUrl : ' /hevc-decode.js ' , }); player . initialize ( videoElement , mpdUrl , true ); Smart detection MediaSource.isTypeSupported() can lie — Firefox on Windows reports HEVC support even without the Video Extension installed. hevc.js actually creates a SourceBuffer to probe; only activates transcoding on failure. When native HEVC works: zero overhead, WASM never loaded. Browser Compatibility Browser + OS Native HEVC hevc.js activates? Transcoding? Safari 13+ (macOS/iOS) Yes (VideoToolbox) No — Chrome/Edge/Firefox (Mac) Yes (VideoToolbox) No — Chrome 107+ (Win, HEVC GPU) Yes (D3D11VA) No — Chrome 107+ (Win, no HEVC GPU) No Yes Yes Edge (Win, with extension) Yes (MFT) No — Edge (Win, no extension) No Yes Yes Firefox 133+ (Win, with extension) Yes (MFT) No — Firefox 133+ (Win, no extension) False positive Yes Yes Chrome/Edge 94-106 No Yes Yes Chrome (Linux, no VAAPI) No Yes Yes Requirements: WebAssembly, Web Workers, Secure Context (HTTPS), WebCodecs with H.264 encoding support. Performance Single-threaded, Apple Silicon: Native C++ WASM (Chrome) 1080p decode 76 fps 61 fps 4K decode 28 fps 21 fps 1080p transcode — ~2.5x realtime WASM reaches 80% of native C++ speed, and 83% of libde265 (a mature 10-year-old HEVC decoder) when both are compiled to WASM. Conformance : 128/128 test bitstreams pixel-perfect against ffmpeg. Zero drift. The Tradeoff The first segment takes 2-3 seconds to transcode — that's the startup latency cost of software decode versus native hardware. After buffering, playback is smooth. This makes hevc.js a good fit for: Streaming platforms with existing HEVC catalogs Infrastructure simplification (single HEVC pipeline, no H.264 fallback) VOD or moderate-latency live Controlled environments (IPTV, B2B) Not ideal for: low-end mobile (CPU/battery), 4K on underpowered machines, or ultra-low-latency live sports. Try It Live demo : hevcjs.dev/demo/dash.html — toggle "Force transcoding" to test the WASM path even if your browser has native HEVC. Install : npm install @hevcjs/dashjs-plugin dashjs GitHub : github.com/privaloops/hevc.js MIT license. Feedback and contributions welcome.