Skip to content

About afmpeg

afmpeg is FFmpeg for Go programs that cannot, or would rather not, shell out. It runs the real thing, current FFmpeg, as a WebAssembly module inside your process, and answers every file the engine opens out of a filesystem you hand it. This page is the short version of why, where and how. The long versions are under Explanation.

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 could not render an in-memory project: a remote cloned into RAM with no local checkout. Every existing Go route was rejected on the way here. Bindings over purego still need a host libav and were immature; CGO bindings break the clean static cross-compile that is half the point of writing the tool in Go; and the stock FFmpeg-in-WebAssembly builds turned out short of the filters and codecs keryx needed, with their I/O aimed at the host filesystem rather than one you supply.

afmpeg closes both gaps. The engine is a maintained, configured FFmpeg build, and the I/O layer answers the guest's filesystem calls out of an afero.Fs of your choosing. keryx now renders its reels through it, in memory and in pure Go.

Where it runs

Anywhere a Go binary runs. There is no native dependency to install and no C toolchain at build time, so the program cross-compiles to a single static binary exactly as it did before FFmpeg was involved. The engine module is fetched at first use from a published ffmpeg-wasi release and verified before it runs, or supplied from a URL of your own.

The filesystem it works over is whatever you give it: an in-memory MemMapFs for a pipeline that never touches disk, the host filesystem when that is what you want, or any other afero backend. It is a server-side and command-line library, not a browser one.

How it works

Three layers, and the middle one is where the work is. The FFmpeg module is current FFmpeg compiled to wasm32-wasi by the sibling project and shipped as a separate artifact, never embedded, so its licence stays at arm's length from yours. The vfs bridge routes the guest's WASI filesystem calls to a sys.FS backed by your afero.Fs, which is what makes "no host disk" true rather than aspirational. The Go API compiles the module once into a reusable Runtime and runs jobs over it, with a typed command builder on top so a transcode is composed from Go values rather than argument strings.

A Runtime is capped, deadlined and serialised by default, because most of the media it will ever see came from someone else. And the runtime sits behind a backend seam: the WebAssembly module is the default, and an opt-in native backend runs the same engine as a signed native subprocess, behind the same API and the same afero I/O, when native-speed encode or the HEVC and AV1 encoders matter more than the sandbox. Architecture has the full flow.

Who is behind it

afmpeg is built and maintained by Matt Cockayne as part of the phpboyscout estate, alongside ffmpeg-wasi, which builds the engine it runs. The library is published on pkg.go.dev, its design lives in the specs on the project wiki, and the branding page has the mark and the palette if you need them.