# Compress Video

Compress a video to a target file size, or by quality, entirely in the browser. Nothing is uploaded.

Runs entirely in the browser through WebCodecs and WebAssembly. No upload, no account, no size limit
imposed by a plan, no watermark — because there is no server involved.

Page: https://life2film.com/tools/video-compressor/

## What matters

- Target a size (10 MB Discord, 16 MB WhatsApp, 25 MB email, 50 MB Nitro Basic) or pick a quality.
- Size = bitrate x duration, so the required bitrate follows from the length of the video.
- Aims at 96% of the limit: a file of exactly 10.00 MB is still rejected by a 10 MB limit.
- Measures the first pass and re-encodes once with a correction if it missed by over 4%.
- Refuses impossible targets and reports the smallest honest size instead.
- Warns when the source is already below the bitrate its resolution needs, re-encoding would enlarge it.
- Measured: a 17.7 MB 1080p file at 12 Mbps came out 9.4 MB against a 10 MB target.

## From code

```js
// npm i mediabunny
import { Input, Output, Conversion, BlobSource, BufferTarget, Mp4OutputFormat, ALL_FORMATS } from 'mediabunny';

// Size is bitrate x duration, so a target size fixes the bitrate.
const OVERHEAD = 0.04;               // container headers and packaging
const AUDIO_BITRATE = 128_000;

export async function compressToSize(file, targetBytes, { width } = {}) {
  const input = new Input({ source: new BlobSource(file), formats: ALL_FORMATS });
  const duration = await input.computeDuration();

  // Aim under the wall: a file of exactly 10.00 MB is still refused by a 10 MB limit.
  const aim = targetBytes * 0.96;
  const videoBitrate = Math.max(
    90_000,
    Math.round((aim * 8 * (1 - OVERHEAD) - AUDIO_BITRATE * duration) / duration),
  );

  const output = new Output({ format: new Mp4OutputFormat(), target: new BufferTarget() });
  const conversion = await Conversion.init({
    input,
    output,
    video: {
      ...(width ? { width } : {}),
      bitrate: videoBitrate,
      hardwareAcceleration: 'prefer-hardware',  // 180 fps vs 34 on an M5 at 1080p
    },
    audio: { bitrate: AUDIO_BITRATE },
  });

  if (!conversion.isValid) throw new Error('Cannot convert this file');
  await conversion.execute();

  return new Blob([output.target.buffer], { type: 'video/mp4' });
}

// Encoders drift, so measure and correct once if the first pass missed.
const blob = await compressToSize(file, 10 * 1024 * 1024);
console.log(blob.size / 1048576, 'MB');
```

## Questions

**Can it hit an exact file size?**

That is what the "fit a size" mode does. A file's size is its bitrate multiplied by its length, so once the length is known the required bitrate is arithmetic. Encoders do not obey a requested bitrate perfectly (rate control drifts with the material) so if the first pass misses by more than a few percent, it measures the miss and re-encodes once with a corrected figure. In practice it lands within a couple of percent of the target.

**Why 10 MB, 16 MB, 25 MB?**

They are the walls people actually hit: 10 MB is the free Discord upload limit, 16 MB is WhatsApp, 25 MB is Gmail and most mail servers, and 50 MB is Discord Nitro Basic. The tool aims just under the limit rather than exactly at it, because a file that is 10.0 MB is still rejected by a 10 MB limit.

**What if the target is impossible?**

It says so before starting, and tells you the smallest honest size for that video. Squeezing an hour of footage into 10 MB would mean a bitrate no resolution survives, a tool that produced an unwatchable file and called it success would be worse than one that refuses.

**How much smaller will my file get?**

The estimate is shown before you press anything, from the settings and the length. Footage straight from a phone or camera is usually recorded far above what it needs, and 60–90% reductions are routine. Something already compressed once has little left to give, and the tool compares your file against what its resolution reasonably needs and warns you when re-encoding would only make it bigger.

**What actually makes a video smaller?**

Fewer pixels and fewer bits per pixel. Resolution is the blunt, reliable lever, 4K to 1080p removes three quarters of the pixels before quality enters into it. Bitrate then decides how much detail survives in what remains. Dropping audio helps a little; it is rarely more than a few percent of a video file.

**Is it uploaded anywhere?**

No. It runs through WebCodecs in your browser, which is what makes this practical: uploading a gigabyte so a server can hand back 200 MB is slower than doing the work locally, and it puts your footage on someone else's disk in the process. No size limit, no queue, no watermark: because there is no server to impose them.

## Related

- [Trim Video](https://life2film.com/tools/video-trimmer/): Drag the handles to the part you want, preview it, save it. Audio stays in sync.
- [Video to MP3](https://life2film.com/tools/video-to-mp3/): Pull the audio out of a video and keep it as MP3, WAV or OGG.
- [Convert Video](https://life2film.com/tools/video-converter/): MP4, WebM and MOV in any direction, or pull the audio out as WAV.
- [Video Splitter](https://life2film.com/tools/video-splitter/): Cut a long video into Reels or Shorts that start on a shot change instead of every 30 seconds, ranked by picture quality.
- [BPM Detector](https://life2film.com/tools/bpm-detector/): Find a track’s tempo and the time of every beat, then send the grid to Resolve, Premiere, Final Cut or Audacity as markers.
- [Scene Detector](https://life2film.com/tools/scene-detector/): Find every cut in a video and get the shot list with thumbnails, timecodes and quality scores.

- The engine as a package: https://life2film.com/engine/
- Site overview for agents: https://life2film.com/llms.txt
- Every tool page is available as markdown by appending `.md` to its address, or by requesting it
  with `Accept: text/markdown`.

---
Life2Film — on-device video montage. https://life2film.com/
