A slim build of FFmpeg for the browser: FFmpeg 5.1.4 with x264, compiled to WebAssembly. It holds only what's needed to turn videos into small, web-ready H.264 MP4s and poster images, so each wasm file is 8 MB instead of the 31 MB of ffmpeg.wasm's full core.
It's the engine of Video Optimizer, and it's published on npm as @matterandmotion/ffmpeg-lite.
| Part | Version |
|---|---|
| FFmpeg | n5.1.4 |
| x264 | ffmpeg.wasm's fork (4-cores branch at 33cac6b), x264 core 164 |
| zimg | 3.0.5 |
| zlib | 1.2.11 |
| Build scripts and bindings | ffmpeg.wasm v12.15 (the release of @ffmpeg/core 0.12.10), changed by ffmpeg-lite.patch |
| Compiler | Emscripten 3.1.40, -O3 -msimd128 |
There are two builds:
| Folder | Build | Use it when | ffmpeg-core.wasm |
Brotli |
|---|---|---|---|---|
mt/ |
Multi-threaded: a pool of 16 threads | The page is cross-origin isolated (crossOriginIsolated is true) |
8.2 MiB | 1.8 MB |
st/ |
Single-threaded | It isn't | 8.1 MiB | 1.8 MB |
- Each folder has
ffmpeg-core.js(an ES module that exportscreateFFmpegCore) andffmpeg-core.wasm;mt/also hasffmpeg-core.worker.js. - Both builds run in browsers and in Node.
execblocks until the command finishes, so in a browser, run it in a worker. - Memory starts at 256 MB and grows as needed, up to 2 GB.
Everything below is enabled by name; everything else in FFmpeg is switched off (--disable-everything).
Reading files. Files only: there are no network protocols.
| Kind | Included |
|---|---|
| Containers | MP4, MOV, M4V and 3GP; Matroska and WebM; AVI; MPEG-TS (.ts, .mts, .m2ts); FLV; animated GIF; image sequences; WAV; MP3; AAC (ADTS) |
| Video codecs | H.264; HEVC (H.265), 8- and 10-bit; VP8; VP9; MPEG-4 Part 2; MPEG-2; Apple ProRes; Avid DNxHD and DNxHR; Motion JPEG; GIF; PNG; raw video; H.263 (comes with MPEG-4) |
| Audio codecs | AAC; MP3; Opus; Vorbis; Apple Lossless (ALAC); AC-3; E-AC-3; PCM (16- and 24-bit integer, 32-bit float) |
Writing files.
| Kind | Included |
|---|---|
| Video | H.264 through x264 |
| Audio | AAC (FFmpeg's own encoder) |
| Images | PNG, JPEG (Motion JPEG), raw frames |
| Containers | MP4, MOV, image sequences, raw, and null (for measuring without writing) |
Processing.
| Filters | What for |
|---|---|
scale, format, setsar |
Resizing, pixel formats and colour matrix conversion (BT.601/BT.709, full and limited range) |
zscale, tonemap |
Converting HDR video (HLG and PQ, BT.2020) to standard range BT.709 |
fps |
Changing the frame rate, and making variable frame rate constant |
transpose, hflip, vflip |
Applying phone rotation |
trim, atrim, setpts, asetpts |
Cutting a clip |
aresample, aformat |
Audio sample rate and channel layout (e.g. 5.1 down to stereo) |
select, crop, split, null, anull |
Picking frames, cropping, and plumbing |
ssim |
Measuring how close an output is to its source |
- Also included: the bitstream filters
aac_adtstoasc,extract_extradata,vp9_superframeandvp9_superframe_split. - Also included: the parsers for H.264, HEVC, VP8, VP9, MPEG-4, H.263, AAC, AC-3, MP3, Opus, Motion JPEG, PNG and GIF.
ffprobeis included alongsideffmpeg.
Anything not listed above, notably:
- Other encoders: H.265 (x265), VP8 and VP9 (libvpx), AV1, MP3 (LAME), Opus, Vorbis, Theora, WebP and GIF. This build only makes H.264 MP4s, AAC audio and PNG/JPEG images.
- AV1 input: FFmpeg 5.1 needs a library such as dav1d to decode AV1, and it isn't included. AV1 videos can't be read.
- Some containers:
- Ogg (
.ogg,.opus,.oga). Opus and Vorbis audio can only be read from WebM, Matroska or MP4. - MXF, which some professional cameras use.
- ASF/WMV, RealMedia and other older formats.
- Ogg (
- Deinterlacing: there's no
yadiforbwdif, so interlaced video (e.g. 1080i camcorder footage) stays interlaced. - Text: subtitles, fonts and text overlays (libass, FreeType, FriBidi, HarfBuzz,
drawtext). - Other filters: overlay, padding, colour correction, sharpening, denoising, audio mixing and volume.
- Image formats as input other than PNG, JPEG and GIF (no WebP, TIFF or BMP).
- Network protocols (HTTP, HTTPS, RTMP and the rest), devices, and
ffplay. - Assembly: FFmpeg and x264 are compiled from C only, with WebAssembly SIMD where the compiler finds it. Encoding is several times slower than a native
ffmpeg. - Hardware acceleration, which WebAssembly doesn't have.
Compared with ffmpeg.wasm's full core (@ffmpeg/core 0.12.10), the libraries removed are x265, libvpx, LAME, libogg, Theora, libvorbis, libopus, libwebp, FreeType, FriBidi, HarfBuzz and libass.
To add something, open an issue. It becomes a new version.
The API is ffmpeg.wasm's core API: exec(...args), ffprobe(...args), setLogger, setProgress, reset, and Emscripten's FS, including WORKERFS for reading a File without copying it into memory.
import createFFmpegCore from '@matterandmotion/ffmpeg-lite/mt/ffmpeg-core.js'
// In a browser, tell it where its wasm (and, for mt, its worker) are:
const core = await createFFmpegCore({
mainScriptUrlOrBlob: `${coreURL}#${btoa(JSON.stringify({ wasmURL, workerURL }))}`,
})
core.setLogger(({ message }) => console.log(message))
core.exec('-i', '/in/clip.mov', /* ... */ '/out/clip.mp4')
core.reset()Set every thread count.
- The
mtbuild has a fixed pool of 16 threads. A command that needs more fails straight away instead of hanging. - Unless told otherwise, FFmpeg picks thread counts from the number of cores, which can need more than 16.
- So always pass the decoder's
-threads(before-i),-filter_threads, and-threadsfor every encoder. - For example, x264 with 8 threads, the decoder with 2 and filters with 2 fit.
- In Node the pool grows instead of failing, so test thread counts in a browser.
The same input gives the same bytes.
- For a given command and thread count, the output is identical in Chromium, Firefox, WebKit and Node.
- x264's thread count changes its output, so the
standmtbuilds give different files of the same size and quality.
./build.sh # both builds into package/st and package/mt (or `st` or `mt`)
node test/smoke.mjs # checks sizes, components and real encodes; needs ffmpeg on PATH
./source.sh 0.1.0 # the complete source, as work/ffmpeg-lite-0.1.0-source.tar.gzbuild.shneeds git, make and Docker with buildx. From cold it takes about 6 minutes per build on 8 cores.- It applies
ffmpeg-lite.patchto ffmpeg.wasm atv12.15and runs that repo's Docker build. - The build is reproducible: building the same version gives byte-identical files. Each release lists their SHA-256 hashes in
SHA256SUMS.
What ffmpeg-lite.patch changes in ffmpeg.wasm's build:
- FFmpeg is configured with
--disable-everything, then only the parts listed above. Only x264, zimg and zlib are linked. - Memory starts at 256 MB, instead of the multi-threaded build's fixed 1 GB, and grows to at most 2 GB. A 4 GB maximum breaks thread creation in the
mtbuild with Emscripten 3.1.40. - The thread pool is 16 instead of 32. A command that needs more fails instead of hanging.
- It also runs in Node, so tests can use the real build.
- The x264 branch, the Dockerfile frontend and the emsdk image are pinned to a commit or digest.
makeruns one job per core.
- Change the version in
package/package.jsonand add it toCHANGELOG.md, in a pull request. - Once it's on
main, run the Build workflow onmainwith publish ticked. - The workflow publishes the package to npm with provenance (npm trusted publishing, no stored token).
- It also creates the
v<version>release, with the package, the source tarball andSHA256SUMS.
Version 0.1.0 was first published as @matterandmotion/ffmpeg-core, from this repository when it was called video-optimizer. That package is deprecated. Its files are identical to @matterandmotion/ffmpeg-lite 0.1.0, and its source is attached to the core-v0.1.0 release.
GPL-2.0-or-later. See LICENSE. FFmpeg is built with --enable-gpl and linked with x264, so the build as a whole is under the GPL.
| Part | Licence |
|---|---|
| FFmpeg | GPL-2.0-or-later as built here (LGPL-2.1-or-later on its own) |
| x264 | GPL-2.0-or-later |
| zimg | WTFPL |
| zlib | zlib |
| ffmpeg.wasm build scripts and bindings | MIT |
The complete source of each release is attached to its GitHub release: every library at the exact version built, plus the build files.