A pure-Go FFmpeg binding that runs on a virtual / in-memory filesystem. No CGO,
no host FFmpeg install, no temp files: FFmpeg is supplied as a separate WebAssembly
module and executed via wazero (a zero-dependency, pure-Go WASM
runtime), with its I/O bridged to an afero.Fs — so
inputs and outputs can live entirely in memory (or any afero backend), and the whole
thing cross-compiles to a single static binary.
Status: released
afmpeg runs real FFmpeg over a virtual filesystem today: the
vfs bridge, the runtime
(New/Run/RunJob/Probe/Close), the Command builder (JobSpec()/RunJob for
the ffmpeg-wasi engine), and both
signature-verified WithModuleRelease and bring-your-own
WithModuleURL module acquisition. Pair it with a released
ffmpeg-wasi module to transcode,
remux, clip, filter, burn in subtitles, edit metadata, extract frames, and read
analysis-filter measurements (ProcessResult.Analysis) — entirely in
memory. For encode- or throughput-bound work there is also an opt-in
native backend — the same engine as a signed native
subprocess, for native-speed encode and the full profile's HEVC/AV1. See the
latest afmpeg release; design rationale
is in the specs under Development (start with
0001).
Why it exists¶
It was extracted from a need in keryx: keryx renders short reels by shelling out to the ffmpeg binary, which needs real files on disk — so it can't render an in-memory project (a remote cloned into RAM, no local checkout). Every existing Go option was rejected: purego bindings are immature and still need host libav; CGO bindings break a clean static cross-compile; and the existing wazero/WASM binding lacks the filters and codecs real workflows need. afmpeg is the "wazero + WASM done right" synthesis — a maintained FFmpeg-WASM build with the codecs/filters we need, a first-class afero virtual-filesystem I/O layer, and a clean Go API. keryx now renders its reels through afmpeg — in-memory, pure Go, with no local checkout.
How it works¶
Three layers — the middle one is the novel engineering:
- The FFmpeg-WASM module — current FFmpeg compiled to
wasm32-wasi, configured to only the codecs/filters needed; shipped as a separate artifact, never embedded. - The afero ↔ wazero vfs bridge (the heart) — routes the guest ffmpeg's WASI
filesystem syscalls to a
sys.FSbacked by the caller'safero.Fs, so reads and writes hit an in-memory filesystem with no host disk touched. - The Go API — compile the module once into a reusable
Runtime, thenRunan ffmpeg invocation over a suppliedafero.Fs; a general command builder layers on top.
The runtime sits behind a backend seam (spec 0028): the WASM module is the default, and an opt-in native backend runs the same engine as a native subprocess for native-speed encode and HEVC/AV1 — same API, same afero I/O, no CGO.
See the architecture explainer for the full flow, and the roadmap for how the specs decompose the build.
Where to go next¶
- Tutorials — learn afmpeg by doing.
- How-to guides — solve a specific task.
- Explanation — the architecture and the why.
- Reference — the Go API and engine artifacts.
- Development — specs and contributor docs.
The Go API reference lives on pkg.go.dev.