DEV Community

Kenji
Kenji

Posted on

I needed audio in Bun, so I skipped fluent-ffmpeg

Bun's Node compatibility layer will run fluent-ffmpeg. That is the honest answer, and for a lot of apps it is good enough.

I still ended up writing a tiny wrapper around Bun.spawn.

The jobs I actually had were boring: transcode a file, pipe a stream, turn whatever the user uploaded into 16 kHz mono PCM for Whisper. fluent-ffmpeg can do all of that, plus video, filters, and a fluent builder I was not going to use. In a Bun-only service I also did not want "it works through the Node compat layer" as the whole story.

So bun-ffmpeg is intentionally small. Audio only. System ffmpeg / ffprobe. Typed helpers. No bundled binary.

What it looks like

Install ffmpeg yourself (brew install ffmpeg, apt install ffmpeg, or set FFMPEG_PATH in Docker). Then:

bun add bun-ffmpeg
Enter fullscreen mode Exit fullscreen mode
import { audio } from "bun-ffmpeg";

await audio("input.mp3", "output.aac", {
  codec: "aac",
  bitrate: "192k",
  channels: 2,
  sampleRate: 44100,
});
Enter fullscreen mode Exit fullscreen mode

The Whisper path is one function. It always emits 16 kHz, mono, pcm_s16le WAV:

import { audioWav } from "bun-ffmpeg";

const wav = await audioWav(
  new Uint8Array(await Bun.file("input.mp3").arrayBuffer()),
);
await Bun.write("input.16k.wav", wav);
Enter fullscreen mode Exit fullscreen mode

Streams work the same way: ReadableStream in, file or chunk callbacks out. If the binary is missing you get an error that tells you to install ffmpeg instead of a raw ENOENT.

Why not fluent-ffmpeg

Need fluent-ffmpeg bun-ffmpeg
Runtime Node, works on Bun via compat Bun only (Bun.spawn)
Surface Video, filters, graphs Audio
Binary Still needs ffmpeg on the machine Same
Shape Fluent builder A handful of functions

If you are muxing video or building filter graphs, keep fluent-ffmpeg. This package is for the case where the runtime is Bun and the media is audio.

Under the hood there is no magic: spawn ffmpeg, pass -i, codec, bitrate, channels, sample rate, and either a path or pipe:0 / pipe:1. The wrapper exists so you do not re-derive those flags in every script.

What it will not do

  • Node.js
  • Video
  • A fluent API
  • Shipping ffmpeg inside the npm tarball

v0.3.0 added FFMPEG_PATH / FFPROBE_PATH for containers, and FfmpegNotFoundError when the binary is not there.

Repo: github.com/KenjiGinjo/bun-ffmpeg

npm: bun add bun-ffmpeg

Top comments (0)