Back to contents·The Bitstream·Vol.1GitHub

Drop in a video or audio file, choose what you want out, and Mediabunny rewraps or transcodes it right here in your browser. Then read the source below — every file is open, and nothing is ever uploaded.

What you can do
Load
  • Drop or pick a file
  • Paste a media URL
  • Queue several files
  • Auto-read metadata
Output
  • MP4 · MOV · MKV · WebM
  • MP3 · WAV · OGG · FLAC
  • ⚡ Remux — copy, no re-encode
  • Quality, target size or bitrate
Transform
  • Trim by waveform
  • Resize · rotate · frame rate
  • Speed 0.5–2× · crop
  • Filters · watermark · captions
Deliver
  • In-browser preview
  • Export several sizes
  • Stream to disk · HLS export
  • Batch convert · join clips
Trust
  • Verify badge (re-opens output)
  • Before → after panel
  • Poster / thumbnail frame
  • 0 bytes uploaded
Fig. 01 — Live ConversionReady
SourceDrop a fileawaiting input
EngineMediabunnyWebCodecs · on-device
Outputchoose a format
Drop a file to convert
or click to browse — MP4 · MOV · MKV · WEBM · MP3 · WAV · OGG · FLAC
Stays on your device · nothing uploaded

Drop or pick several files to batch-convert or join them. URLs stream via Mediabunny's UrlSource — must be a direct file link on a CORS-enabled host (not a YouTube/streaming page).

Drop a file to see the output formats your browser can produce.
Files are decoded, transcoded and muxed entirely on your device — nothing is uploaded.
Fig. 02 — Why Mediabunnyvs the usual alternatives
Live — measured on your file, just now

Convert a file above and these fill with real numbers from your own device — processing time, throughput, and how many times faster than real-time it ran. No synthetic benchmark.

Mediabunny vs the usual ways to convert
Mediabunnyffmpeg.wasmServer transcodeMediaRecorder
Where it runsYour browser · WebCodecsYour browser · wasmYour serverYour browser
Download to start~156 KB gz ¹~25–31 MB wasm ²0 · built-in
SpeedHW-accelerated; instant remuxSoftware codecs, slowerFast, but + up/downloadReal-time only (1×)
Uploads your file?NoNoYesNo
COOP/COEP headersNot neededNeeded for threads ²Not needed
CostFreeFree$ server timeFree
Works offlineYesYesNoYes
Format controlFull APIFull · CLI stringFullMinimal

¹ Full library, gzipped, before tree-shaking — measured from the installed package; a convert-only import is smaller. ² ffmpeg.wasm core size & cross-origin-isolation requirement per its project docs. Remaining cells are qualitative.

What it reads → writes, and what copies vs re-encodes
ReadsMP4 · MOV · MKV · WebM · MP3 · WAV · OGG · FLAC · AAC
WritesMP4 · MOV · MKV · WebM · MP3 · WAV · OGG · FLAC

When the source codec already fits the target container, Mediabunny copies the packets straight through — instant and lossless, no re-encode. Otherwise it transcodes with WebCodecs.

Small, private, header-free
156 KBgzipped library ¹
~30 MBffmpeg.wasm core ²
0bytes uploaded
NoneCOOP/COEP headers

Everything runs on-device — turn Wi-Fi off and it still converts. Nothing leaves the tab.

FIG. 02 — Measured live where it's your file, cited where it's the alternative. No ffmpeg.wasm is loaded on this page; the comparison is for reference.
The source — every file, nothing hidden
Fig. 01a — Files
convert-demoread-only
Fig. 01b — CodeTS
convert-demo/src/convert.ts
1import {
2 Input, Output, Conversion, ALL_FORMATS,
3 BlobSource, BufferTarget,
4 Mp4OutputFormat, QUALITY_HIGH,
5} from 'mediabunny';
6import { videoProcess } from './transforms';
7 
8// One conversion with the full option set — all in the browser.
9export async function convert(file, opts = {}) {
10 const input = new Input({ source: new BlobSource(file), formats: ALL_FORMATS });
11 const output = new Output({ format: new Mp4OutputFormat(), target: new BufferTarget() });
12 
13 const conversion = await Conversion.init({
14 input, output,
15 trim: opts.trim, // { start, end } in seconds
16 video: {
17 codec: 'avc',
18 bitrate: opts.bitrate ?? QUALITY_HIGH, // a number (target size) or QUALITY_*
19 width: opts.width, height: opts.height, fit: opts.fit,
20 rotate: opts.rotate,
21 crop: opts.crop, // { left, top, width, height }
22 frameRate: opts.frameRate,
23 alpha: opts.keepAlpha ? 'keep' : 'discard',
24 process: videoProcess(opts), // filters / watermark / speed
25 },
26 audio: opts.mute
27 ? { discard: true }
28 : { codec: 'aac', bitrate: opts.bitrate ?? QUALITY_HIGH,
29 numberOfChannels: opts.channels, sampleRate: opts.sampleRate },
30 tags: opts.stripMetadata ? {} : undefined, // {} strips, undefined copies
31 });
32 
33 if (!conversion.isValid) {
34 throw new Error(conversion.discardedTracks.map((t) => t.reason).join(', '));
35 }
36 conversion.onProgress = opts.onProgress; // 0 → 1
37 await conversion.execute();
38 
39 // The finished file — it never left the device.
40 return new Blob([output.target.buffer], { type: 'video/mp4' });
41}
The real source — click any file on the left to read it. Runs as-is, no ffmpeg.wasm, no server.