Using ffmpeg.wasm for Front-End Audio and Video Compositing

An interactive walkthrough of the article’s core ideas: browser video-generation options, local ffmpeg.wasm loading, single audio/video muxing, delayed multi-audio mixing, and the practical limits of running FFmpeg inside the browser.

1. Browser Recording Is Convenient, but Output Mixing Is Limited

Canvas capture can create a stream from visuals, but it does not solve independent audio placement and mixing.

Code:

const canvas = document.querySelector('#scene');
const stream = canvas.captureStream(30);

const recorder = new MediaRecorder(stream, {
  mimeType: 'video/webm'
});

recorder.start();
// This captures the canvas frames only.
// Separate audio clips still need another pipeline.

Result:

Canvas frames No independent audio
Capture status

Waiting. The animation can be captured as frames, but the separate audio timeline is empty.

2. Load ffmpeg.wasm from Local Core Files

The article recommends hosting the FFmpeg runtime files locally because the core file is large and slow to fetch from a remote CDN.

Code:

const { createFFmpeg, fetchFile } = FFmpeg;

const ffmpeg = createFFmpeg({
  corePath: './ffmpeg-core.js',
  log: true
});

await ffmpeg.load();

Result:

Local runtime folder
ffmpeg.min.jsloader
ffmpeg-core.jsruntime
ffmpeg-core.worker.jsworker
ffmpeg-core.wasm22.4 MB
Not loaded 0%

This is a local simulation of the loading step. The important idea is that the browser must fetch and initialize a large WASM runtime before any command can run.

3. Composite One Silent Video with One Audio File

The first useful FFmpeg step is mapping the original video stream and the new audio stream into a single MP4 output.

Code:

await ffmpeg.run(
  '-i', 'bj.mp4',
  '-i', 'record.mp3',
  '-c:v', 'copy',
  '-c:a', 'aac',
  '-map', '0:v:0',
  '-map', '1:a:0',
  'output.mp4'
);

Result:

output.mp4
0:v:0 bj.mp4 copied
1:a:0 record.mp3 encoded AAC
0.0s / 12.0s
ffmpeg -i bj.mp4 -i record.mp3 \ -c:v copy \ -c:a aac \ -map 0:v:0 \ -map 1:a:0 \ output.mp4

4. Understand adelay and amix in filter_complex

The key leap is delaying individual audio streams first, then mixing those delayed streams into one output label.

Code:

const filter =
  '[1]adelay=2000|2000[aout1];' +
  '[2]adelay=10000|10000[aout2];' +
  '[aout1][aout2]amix=2[aout]';

await ffmpeg.run(
  '-i', 'video.mp4',
  '-i', 'voice-1.mp3',
  '-i', 'voice-2.mp3',
  '-filter_complex', filter,
  '-map', '0:v:0',
  '-map', '[aout]',
  'output.mp4'
);

Result:

[1] voice-1.mp3
adelay
2000ms
[2] voice-2.mp3
adelay
10000ms
[aout1] + [aout2]
amix=2
[aout]
voice-1 starts 2s
voice-2 starts 10s
[aout] mix mix

5. Composite Four Delayed Audio Clips into One Video

This mirrors the article’s final working pattern: delay each input, mix all delayed labels, and map the mixed audio into the output.

Code:

await ffmpeg.run(
  '-i', 'zhangxinxu.mp4',
  '-i', '1.mp3',
  '-i', '2.mp3',
  '-i', '3.mp3',
  '-i', '4.mp3',
  '-filter_complex',
  '[1]adelay=2000|2000[aout1];[2]adelay=10000|10000[aout2];[3]adelay=15000|15000[aout3];[4]adelay=20000|20000[aout4];[aout1][aout2][aout3][aout4]amix=4[aout]',
  '-c:v', 'copy',
  '-c:a', 'aac',
  '-map', '0:v:0',
  '-map', '[aout]',
  'output.mp4'
);

Result:

Current time: 0.0s Active clips: 0
zhangxinxu.mp4 + [aout]
0:v:0 video copied
1.mp3 2s
2.mp3 10s
3.mp3 15s
4.mp3 20s
[aout] mix mix mix mix
70%
70%
70%
70%

6. Practical Limits: Size, Startup Time, and Memory

The article’s conclusion is pragmatic: ffmpeg.wasm is powerful, but large downloads and memory pressure make it better for internal tools or selected users.

Code:

if (!window.WebAssembly) {
  throw new Error('WebAssembly is not available');
}

try {
  new WebAssembly.Memory({ initial: 256 });
  console.log('Enough memory for this demo');
} catch (error) {
  console.error('WebAssembly.Memory(): could not allocate memory');
}

Result:

Core size

Large WASM payloads block the feature until the runtime is downloaded and initialized.

Startup wait

Even on a local network, loading can feel slow compared with ordinary page scripts.

Memory risk

Some devices cannot allocate enough WebAssembly memory for FFmpeg workloads.

WebAssembly availability
Checked in this browser
Checking
Memory allocation probe
Attempts a small browser-safe allocation
Checking
Recommended use
Best for controlled users, VIP features, or internal production tools
Selective