Open source · MIT license

hevc.js

Play HEVC/H.265 in any browser without native support.
No plugin. No server changes. No double-encoding.

A from-scratch HEVC decoder compiled to WebAssembly, with a drop-in plugin for dash.js. Transcodes HEVC to H.264 client-side via WebCodecs.
Chrome, Edge, and Firefox with WebCodecs support.

HEVC WebAssembly Decoder Performance

0
server changes
works on any static file server
128/128
pixel-perfect
vs ffmpeg on every test
60fps
1080p decode
WASM, single-thread
236KB
WASM binary
gzipped, zero dependencies
716
pages of spec
ITU-T H.265 v8, transcribed to C++17
83%
of libde265 speed
a decoder with 10 years of optimizations

How HEVC to H.264 client-side transcoding works

When the browser can't play HEVC natively, hevc.js intercepts the media pipeline and transcodes to H.264 — transparent to the player.

// The player (hls.js, dash.js) doesn't know.
// It requests HEVC segments.
// MSE intercept delivers H.264 to the SourceBuffer.
// Seek, ABR, live streams — everything works.

HEVC stream  ──►  Web Worker  ──►  H.264 to MSE
              demux → decode → encode → mux
Tradeoff: the first segment takes 2-3s to transcode (vs instant with native hardware decode). Once buffered, playback is smooth. This is inherent to any software HEVC decoder — when native support is available, hevc.js detects it and does nothing.

See it play

A real HEVC/H.265 DASH stream, decoded in WebAssembly and transcoded to H.264 in your browser as it plays.

Detecting browser HEVC support…

Loading…

Transcoding runs entirely in your browser — no data is uploaded. For 720p / 1080p / 4K test streams and the Shaka Player build, see the dash.js and Shaka demos.

Why you need an HEVC browser fallback

You serve HEVC because it saves bandwidth — 30-50% smaller than H.264 at the same quality. But some browsers still can't play it. Those users get a black screen, a fallback to lower quality, or nothing.

The usual fix is double-encoding: serve HEVC for browsers that support it, H.264 for the rest. That means maintaining two encoding pipelines, two sets of manifests, and twice the storage on your CDN.

hevc.js takes a different approach. Keep your single HEVC pipeline. For Chrome and Edge without native HEVC, the transcoding happens client-side, in a Web Worker, invisible to your player and your infrastructure.

hevc.js is a browser compatibility solution for H.265/HEVC. Instead of maintaining a separate H.264 encoding pipeline as a fallback, the decoder handles HEVC to H.264 transcoding client-side, directly in the browser via WebAssembly.

The decoder is a direct implementation of the ITU-T H.265 spec — 716 pages of it, transcribed to C++17 and compiled to WebAssembly. Every frame is validated pixel-perfect against ffmpeg.

HEVC plugin for dash.js

HEVC playback for dash.js. 3 lines of code.
Activates only when native HEVC is unavailable.

dash.js

@hevcjs/dashjs-plugin
import { attachHevcSupport }
  from '@hevcjs/dashjs-plugin';

await attachHevcSupport(player);
npm install @hevcjs/dashjs-plugin

Spec conformance

Implemented per ITU-T H.265 v8 — transcribed directly from the spec, not from ffmpeg or libde265.

CABAC arithmetic decoding (§9.3)
35 intra prediction modes (§8.4)
Inter prediction — merge, AMVP, TMVP (§8.5)
8-tap luma / 4-tap chroma interpolation
Weighted prediction — default + explicit
Inverse transform — DCT 4-32, DST 4
Deblocking filter + SAO (§8.7)
10-bit decoding (Main 10 profile)
Multi-slice (dependent + independent)
WPP (Wavefront Parallel Processing)

Reaches 83% of libde265 speed — a decoder with 10 years of optimizations.