Black code text survived CRF 33 in a lecture recording, red text was soft at CRF 18
The question I wanted answered about lecture-style screen recordings: how high can CRF go before slide text gets hard to read? The usual rule is that lower CRF means better quality, which doesn't say whether a line of code survives when the goal is a smaller file.
So I ran a CRF ladder on one recording and measured the text directly. Black code text barely moved until CRF 33 and only dropped clearly at 43. Red text on the same frame was already soft at CRF 18, and that turned out to have little to do with CRF.
Test setup
The recording is not a real class. It's a 4-minute mock lecture recording I built myself (slides flipping, mouse, typing, a scrolling handout), 1920×1080, 30 fps, encoded at a constant 12 Mbps that I picked as an assumption (not any recorder's default), 360.0 MB, no audio.
The screen sits still for about 78% of its length, and since it was rendered from screenshots it has no noise, so a real lecture capture would compress worse.
For the test I cut 20 static seconds from the lossless master, encoded them with x264 preset medium at CRF 18 to 43 in steps of 5, and compared the frame at second 60 against the original with ffmpeg's SSIM filter on two crops: black code and small red text. One run per setting, Apple M4 Mac.
Black code text held until CRF 33
SSIM for the code block went 0.9998 at CRF 18, 0.9993 at 23, 0.9988 at 28 and 0.9967 at 33, while the clip shrank from 564,096 bytes to 319,617, about 43% smaller.
After that the curve bends: 0.9886 at CRF 38 and 0.9619 at 43, where the clip is 186,964 bytes. The drop from 33 to 43 is more than ten times the whole drop from 18 to 33.
I don't have a clean threshold for "unreadable", so I read 33 as where the curve bends, not a hard line. My guess is that text edges are high-frequency detail and those coefficients go first as the quantizer step grows, but that's inferred from the curve's shape, not isolated.
Red text was already soft at CRF 18
This is where I didn't understand my own table at first. At CRF 18 the red text scored 0.926, lower than the black code at CRF 43. Then 0.924, 0.920, 0.902, 0.884 and 0.854 up to CRF 43.
Going from CRF 23 down to 18 bought 0.002 of SSIM and cost 19% more bytes, so CRF simply wasn't the knob.
The color is lost before quantization: the default pixel format, yuv420p, keeps color at half resolution in both directions, so a red stroke one or two pixels wide gets averaged with the white around it and its edges go gray.
Switching to 4:4:4 brought red text back for 2% more bytes
I re-ran the ladder with -pix_fmt yuv444p. At CRF 23 the red text went from 0.924 to 0.988 and the clip grew from 473,123 to 482,793 bytes, 2.0% more, while black code barely changed (0.9992 against 0.9993).
The pair I keep coming back to: 4:4:4 at CRF 33 is 314,729 bytes with red text at 0.947, and 4:2:0 at CRF 18 is 564,096 bytes with red text at 0.926. A smaller file with better red text.
The catch I haven't solved is playback: 4:4:4 H.264 needs the High 4:4:4 Predictive profile, and I haven't tested whether browsers, phones or a school's video platform will play it.
Pushing CRF saves little on a whole recording
On the full recording, CRF 23 already took 360.0 MB down to 5.2 MB, because the constant-bitrate original spends a median 50,002 bytes on every static frame and the re-encode spends 77.
Going from CRF 23 to 28 saved another 17.2%, against 43.4% on a camera clip from Wikimedia Commons (CC BY-SA, re-encoded). Lowering resolution was a worse trade: 720p made the file 30.4% smaller, but small handout text fell from SSIM 0.998 to 0.880.
The last thing I tried on the full file was ImgIng https://imging.ai/ , which compresses video locally in a desktop browser and also handles batches and H.265. Its smart-recommend setting gave 20.2 MB with handout text at the same level as HandBrake's Fast 1080p30 preset (6.0 MB), while its size-first setting dropped to 1080×608 at 24 fps, 10.8 MB, and the small text visibly blurred.
So for a lecture recording I'd keep the resolution, start at CRF 23 to 28, and treat 33 as the ceiling for black text. With colored text and playback I control, I'd switch to yuv444p before touching CRF. Without that control I'd stay in 4:2:0 and accept soft red text, since a lower CRF won't fix it.
This is one clean synthetic recording, single runs, x264 medium only, so for your own file cut a static stretch, encode the same ladder, crop one black and one colored text region, and run ffmpeg's ssim filter against the source.
Comments
No comments yet. Start the discussion.