halfsize

halfsize / Target size

Compress a video to a size you name

A target size is not a setting. It is a budget: every second of video gets bytes × 8 ÷ duration bits, minus audio and container overhead. Once you see the budget, the quality question answers itself, and the targets that cannot work stop looking like encoder problems.

What each target leaves for video

Video bitrate after a 128 kbps audio track and about 2% container overhead:

Duration5 MB10 MB20 MB25 MB50 MB100 MB
15 s2.5 Mbps5.1 Mbps10.3 Mbps12.9 Mbps26.0 Mbps52.1 Mbps
30 s1.2 Mbps2.5 Mbps5.1 Mbps6.4 Mbps12.9 Mbps26.0 Mbps
1 min525 kbps1.2 Mbps2.5 Mbps3.1 Mbps6.4 Mbps12.9 Mbps
2 min199 kbps525 kbps1.2 Mbps1.5 Mbps3.1 Mbps6.4 Mbps
5 min—133 kbps395 kbps525 kbps1.2 Mbps2.5 Mbps
10 min——133 kbps199 kbps525 kbps1.2 Mbps

A dash means the target is below what the tool will attempt: under 80 kbps for video, nothing looks like video. It reports the smallest size the duration can hold instead.

Read the table as a sentence: anything at 100 kbps per second is a slideshow. 500 kbps is fine for 540p and people talking. 1.2 Mbps carries 720p. 2.5 Mbps is comfortable 720p or modest 1080p. Above 5 Mbps you are usually spending bytes on film grain.

What each target actually costs

Measured on the same 60-second 1080p30 source at 18 Mbps (136 MB), encoded locally and compared against the source frame by frame with VMAF:

TargetOutputResolutionVMAFWhat it looks like
20 MB19.45 MB1280×72088.3slight softening
16 MB15.59 MB960×54084.8soft, still clean edges
10 MB9.77 MB960×54082.9soft, motion starts to smear
5 MB4.93 MB640×36072.0visible loss, fine text dies

The resolution column is the point. The tool drops resolution before it starves the encoder, because 640×360 at 500 kbps looks better than 1920×1080 at 500 kbps. If you would rather keep the frame size, --no-downscale exists, and it will be softer.

One command per target

$ npx halfsize holiday.mp4 --to 10mb

  target     10.0 MB
  plan       H.264  960x540  1.18 Mbps  audio 128 kbps  2-pass
  scale      1920x1080 → 960x540

  136 MB  →  9.77 MB   14x smaller   2.3% under target
  VMAF 82.9   SSIM 0.958   soft, motion smears

Any number works: --to 25mb, --to 50mb, --to 700k, --to 1.5gb. Units are k, mb and gb; decimal, not binary, because that is how the caps are written.

If the target is larger than the source, stop. Re-encoding upward spends bitrate the source does not have and gives you a lossy copy of a lossy original. 50 MB and 100 MB targets only make sense for clips that came in bigger than that.

Why a target can be impossible

Ten minutes in 10 MB is 133 kbps for picture and sound together. No encoder fixes that; the arithmetic does not care which codec you pick. Since 0.1.9 the tool checks the budget before encoding and reports the smallest size the duration can hold, so you learn it in a second instead of ten minutes.

The honest fixes, in order: cut the clip, drop the audio (--no-audio buys back about 1 MB per minute at 128 kbps), or lower the resolution and accept it.

FAQ

How many minutes of video fit in 20 MB?

About two minutes at 1.2 Mbps (720p) or five at 400 kbps (low-motion 540p). Ten minutes is 133 kbps and looks it.

Can I hit a size larger than the original?

Yes, but there is nothing to gain: you spend bitrate the source does not have. Keep the original.

Is the byte count exact?

No. It lands 1–3% under on purpose, because caps are decimal and file managers show binary. An overshoot triggers one corrected re-encode.

Does it work without a size in mind?

Yes, --crf 20 targets a quality level instead. You lose the size guarantee, which is the whole point of a cap.

Where does the audio budget go?

128 kbps by default, kept because speech at 64 kbps sounds thin. --no-audio removes it entirely.

Related