Grain does not survive the delivery encode
A shader looks correct in the engine, gets captured, gets encoded, and arrives on a store page looking soft and blotchy. Nothing in the shader changed. The encode is a stage in the pipeline and it has opinions about high frequencies.
The left half is where per-pixel grain goes: mostly gone, and what is left has turned into low-frequency blotching that is worse than no grain at all. The right half still has grain, because grain at three pixels is a frequency the encoder keeps.
To be clear about what is on screen: the right half is a simulation, not an encoder. It block-averages at 8×8 and attenuates each pixel's deviation from that average, which is the visible consequence of dropping high-frequency coefficients. It is directionally right and it is not x264.
Why per-pixel noise is the first thing to go
Every mainstream codec transforms 8×8 or 16×16 blocks into frequency coefficients, then quantizes them, and the quantizer is coarser for high frequencies because that is where the eye is least sensitive. Per-pixel noise is entirely high frequency by construction. It occupies the coefficients the encoder is most willing to zero.
Worse, it does not go quietly. Grain is incompressible, so the encoder spends bits on it until it hits the rate cap and then starts taking bits from everything else. A grainy capture at a fixed bitrate has a softer image and no grain, which is the opposite of both goals.
The scales that survive
- 1px: effectively gone. Costs bits, delivers nothing.
- 2px: partially survives, usually as unattractive clumping.
- 3 to 4px: survives recognizably at typical bitrates. This is the range to author in if the grain has to be baked.
- 8px and up: survives easily, and is no longer grain. It is texture, and it will read as such.
The exact numbers move with bitrate and resolution, which is the reason to test rather than to take the table. The shape of the answer does not move.
The better answer is to not bake it
Grain is cheap to generate and expensive to transmit, which is an unusually clear argument for generating it at the far end:
- In a game, add grain after everything else, at the display resolution. It is three instructions and it never goes through a codec at all.
- For video, encode clean and let the player add grain. AV1 has a film grain synthesis mode built for exactly this: the encoder measures the grain, strips it, sends the parameters, and the decoder puts it back. Enormous bitrate saving for a better-looking result.
- If it must be baked, bake it coarse, and check it on the delivered file rather than on the master.
The same logic, applied to everything else fine
Grain is the clearest case but not the only one. Anything that lives at the pixel scale is negotiating with the encoder:
- Dither. Applied before encode, it is discarded and the banding it was hiding comes back. Dither belongs at the display, after everything.
- One-pixel outlines and hairlines. They survive better than noise because they are coherent, but they soften, and a design that depends on a crisp 1px line will not look like the mock.
- Fine hatching and halftone. Frequently lands right in the range that turns into moire.
- Sharp high-contrast edges on a moving background, which is where you see mosquito noise: the encoder cannot afford the coefficients and rings around the edge.
The check that costs ten minutes
Encode a capture at the bitrate the platform actually uses, then look at the encoded file rather than the master. Not a screenshot of the timeline: the delivered artifact. Most of these problems are invisible until that step and obvious immediately after it.
Rules of thumb
- Per-pixel grain does not survive a delivery encode. It costs bits and arrives as blotching.
- If it must be baked in, author it at three to four pixels and verify on the encoded file.
- Better: generate grain at the display, or use AV1's grain synthesis and send a clean picture.
- Dither belongs after delivery too, for exactly the same reason.
- Grain steals bits from the rest of the image. A grainy capture at a fixed rate is softer everywhere.
- Judge every fine detail on the delivered file, never on the master.
One email when something new goes up. No newsletter, no schedule, nothing else.
Double opt-in, so watch for a confirmation email. Unsubscribe any time.