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:
| Duration | 5 MB | 10 MB | 20 MB | 25 MB | 50 MB | 100 MB |
|---|---|---|---|---|---|---|
| 15 s | 2.5 Mbps | 5.1 Mbps | 10.3 Mbps | 12.9 Mbps | 26.0 Mbps | 52.1 Mbps |
| 30 s | 1.2 Mbps | 2.5 Mbps | 5.1 Mbps | 6.4 Mbps | 12.9 Mbps | 26.0 Mbps |
| 1 min | 525 kbps | 1.2 Mbps | 2.5 Mbps | 3.1 Mbps | 6.4 Mbps | 12.9 Mbps |
| 2 min | 199 kbps | 525 kbps | 1.2 Mbps | 1.5 Mbps | 3.1 Mbps | 6.4 Mbps |
| 5 min | — | 133 kbps | 395 kbps | 525 kbps | 1.2 Mbps | 2.5 Mbps |
| 10 min | — | — | 133 kbps | 199 kbps | 525 kbps | 1.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:
| Target | Output | Resolution | VMAF | What it looks like |
|---|---|---|---|---|
| 20 MB | 19.45 MB | 1280×720 | 88.3 | slight softening |
| 16 MB | 15.59 MB | 960×540 | 84.8 | soft, still clean edges |
| 10 MB | 9.77 MB | 960×540 | 82.9 | soft, motion starts to smear |
| 5 MB | 4.93 MB | 640×360 | 72.0 | visible 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.