DEV Community

Lank_M
Lank_M

Posted on

The knob I reached for first was the only one that hurt

I spent an afternoon compressing one screen recording about fifteen times, and the thing I learned has nothing to do with which tool is best. It's about the order you turn the knobs in.

Here's the short version. Dropping the resolution is the knob most people reach for first. On this clip it was the only step that damaged small text — and at the middle setting it also produced a larger file than leaving the resolution alone. Swapping the codec, which I got to last, cut the size by another 67% and cost 0.74 dB on the text region.

The clip is not a real meeting or a real product demo. I built it: a fictional warehouse admin UI, made-up SKUs, made-up product names and warehouse names. 62 seconds, 1920×1080, 30 fps, with a real AAC track. It deliberately contains 11 px monospaced SKU codes and 13 px product names, because small text is the first casualty when a screen recording gets squeezed.

Getting it to 295.7 MB (310,087,773 bytes) took effort. Synthetic frames are pixel-identical in the static regions, so x264 skips those macroblocks wholesale: CBR at 42 Mbps gave me 18 MB, CRF 12 gave me 10 MB, even QP 6 only landed at 13.5 MB. What finally produced a three-digit file was QP 14 with keyint=1 — all-intra, no inter-frame prediction at all, which happens to be exactly what QuickTime-style screen recorders emit in their light-compression modes.

That matters, because it means the headline number below is riding on a very specific premise: the source had never been inter-frame encoded. If your recorder already did a normal compression pass, your file is probably 10–18 MB to begin with and there is far less to reclaim.

The tool is ImgIng's in-browser video compressor (https://imging.ai/). I picked it because preset, resolution, frame rate and output format are four independent dropdowns, so I can pin three and move one — which is the only way to attribute anything. Everything ran on one M4 Mac through the browser's hardware H.264 encoder, so treat every timing below as machine-specific.

Step 1: change nothing

Import, hit compress, touch nothing else. 9.2 seconds later: 21.34 MB, 7.21% of the original, −92.8%.

What it didn't do is the interesting part. Output stayed 1920×1080 at 30 fps, all 1860 frames present, 62.08 s, audio intact. The entire cut came out of bitrate: 40 Mbps down to 2767 kb/s. Sampled-frame PSNR 46.17 dB, SSIM 0.9989; the text region alone measured 43.45 dB. Zoomed 3× at second 48, the SKU codes are character-for-character identical to the source.

A 295.7 MB screen recording: what each of five steps actually saves

A sane person stops here. I wanted to know what was left in those 21 MB.

Step 2: the presets are the bitrate knob

There is no bitrate field in this UI — the four presets are the bitrate. With resolution and frame rate explicitly pinned to "keep original", all four outputs came back 1920×1080 @ 30 fps, differing only in bitrate: 2767 / 2502 / 2212 / 1925 kb/s, giving 21.34 / 20.32 / 18.07 / 15.84 MB. Walking the whole range saves another 29%.

The cost: 0.42 dB on the text region, and my sharpness metric (mean horizontal gradient over the table area, 4.15 on the source) sat at 4.13 for all four.

Two side findings. The "size first" preset advertises "long edge ≤1080 px, ≤24 fps", yet with the dropdowns explicitly set to keep original it still emitted 1920×1080 @ 30 fps and only lowered the bitrate — explicit selections override the preset's own caps. And the preset named "smart" produces a larger file than the one named "visually near-lossless" (21.34 vs 20.32 MB), which is backwards from what the names suggest.

Step 3: the turn — resolution costs quality and adds bytes

Long edge 1920 → 1280. Pixel count down to 44%. I expected a solid win.

I got 25.73 MB, against 20.32 MB for the same preset at original resolution. 26.6% bigger. I re-checked the parameters before I believed it. The cause is in the bitrate: the 1280 option is paired with 3200 kb/s, higher than the 2502 kb/s used at original resolution. Fewer pixels, more bits per second.

This isn't mysterious if you think of resolution and bitrate as two separate budgets — lowering one does not lower the other. What is unpleasant is the quality side: text-region PSNR fell from 43.41 to 29.13, a 14.28 dB drop in one step, and sharpness went 4.13 → 2.81. Go one further to long edge 854 and the SKU codes are no longer readable — not soft, actually illegible.

Table text at second 48, zoomed: only the resolution steps hurt it

So the awkward coincidence at the center of this whole exercise: the only step that damaged the text is also the only step that gave back bytes.

Step 4: frame rate — and the blind spot in my own method

The dropdown offers 60 / 30 / 24 only, no 15, so the test is 30 → 24. Size went 20.32 → 17.39 MB, −14.4%. PSNR stayed at 46.11. Sharpness stayed at 4.13.

That looks free, and I want to be explicit that it isn't — it only looks free because sampled-frame PSNR is structurally blind to dropped frames. I sample both files at fixed wall-clock timestamps; at those instants both show the same static table, so of course they match. The actual frame count tells the real story, and since this build of imageio-ffmpeg ships no ffprobe, I had to decode and count packets:

def nframes(path):                    # no ffprobe in this build; decode and count
    err = subprocess.run([FF,'-hide_banner','-i',path,'-map','0:v:0','-f','null','-'],
                         capture_output=True, text=True).stderr
    return int(re.findall(r'frame=\s*(\d+)', err)[-1])
# 1860 -> 1488: 372 frames gone, exactly 20%
Enter fullscreen mode Exit fullscreen mode

372 frames are gone. What they carried was cursor motion and scroll continuity, and I measured that with exactly zero metrics this round. So: 14.4% smaller, and I can't tell you what it cost.

Step 5: the codec, which I should have tried first

Preset, resolution and frame rate all pinned; only the container/codec changed. VP9 came back 24.32 MB — 19.7% larger than H.264, for 0.68 dB. AV1 came back 6.40 MB: same 1920×1080 @ 30 fps, same 1860 frames, 67% smaller than H.264, text-region PSNR down only 0.74 dB, sharpness 4.09.

One caveat per line, because a single clip is a single clip: VP9 losing here does not make VP9 worse in general, and AV1 winning here does not make AV1 universally smaller. AV1 is also gated behind a runtime capability check — it passed and ran on this machine; what the UI does when the check fails, I never saw, because both Chromium builds I had reported support.

I did load all three outputs into a plain <video> element and verified decode plus actual playback progress across three engines (Chromium 149, WebKit 26.5, Firefox 151) — nine for nine, no media errors. That is local playback only. What any upload platform's re-encode does to these files, I tested on exactly zero platforms. If the recipient's environment is unknown, MP4/H.264 is still the answer you don't have to think about.

Codec first, then frame rate, then walk the presets down, and touch resolution last. The first three cost under 1 dB combined on this clip and left sharpness untouched; resolution is both the only text-killer and, at the middle step, a net loss on size.

The scope of that conclusion is narrow, and I'd rather say so than have someone quote it at me later. Same default preset, two other self-built samples: a slide-style meeting recording saved 40.7%, and a clip where the whole frame changes every frame saved 45.9% with PSNR falling to 26.52 dB — visible degradation, not a metric artifact. "Screen recordings compress by 90%" is a property of screen recordings — large static areas, lots of text, motion confined to a small region — not of video compression.

One last detail I didn't expect to like. I fed an already-compressed output back in. The tool actually transcoded the whole thing again, produced 11.94 MB (2% smaller than the input), decided that was below its stated 12% threshold for that preset, and handed the original back byte for byte — same SHA-256 in and out, with the UI saying the source was kept as-is. Most tools would have given me the marginally smaller, marginally worse file and let me discover the damage later.

Top comments (0)