Live demo
Frames processed per second, summed over all videos. Same videos, same filter and the same extra work in both cases.
Each bar is one main-thread animation frame. Tall red bars are frames the page could not render in time.
Why not just WebCodecs?
WebCodecs gives you a decoder, not a player. HTMLVideoElement already does everything around it,
and for frame processing that is usually exactly what you need. Its only problem is that it does not exist in
workers. This package fixes that.
VideoDecoder (WebCodecs)
- You demux every container yourself (mp4box.js, a WebM parser, …).
- You configure codecs, feed chunks and manage decoder queues.
- Seeking means finding keyframes and decoding forward to the target time.
- Streaming, range requests and buffering are up to you.
<video> in a worker (this package)
- Every format the browser plays: MP4, WebM, MOV, MSE streams, HLS where supported.
- No demuxing: set
src, thenplay()or setcurrentTime. - Frame-accurate
seekedwith the decoded frame ready in the handler. - Frames reach the worker as zero-copy
VideoFrames for Canvas, WebGL or WebGPU.
How it works
The main thread keeps the real element. The worker gets an object with the same API: properties and calls are
forwarded over postMessage, state and events come back, and every decoded frame is transferred as
a VideoFrame. The worker keeps the current frame, so it can read it synchronously.
main.js
import { createVideoHost } from "html-video-element-webworker-polyfill/host";
const worker = new Worker("./worker.js", { type: "module" });
createVideoHost(worker);
worker.js
import { polyfillDocument } from "html-video-element-webworker-polyfill/worker";
import { polyfill2dCanvasApi } from "html-video-element-webworker-polyfill/canvas2d";
polyfillDocument();
polyfill2dCanvasApi();
const video = document.createElement("video");
video.src = "movie.mp4";
video.currentTime = 12;
video.addEventListener("seeked", () => {
ctx.drawImage(video, 0, 0);
const pixels = ctx.getImageData(0, 0, w, h);
});