Grain for plastic skin
Give it a take that is right in every way except that skin and surfaces look too smooth (a typical note on H3 Max and other fast models); get back the same take with moving, brightness-neutral film grain, at a strength picked from a ladder. It is the cheapest fix for a texture note: seconds of ffmpeg instead of a re-render. It won't fix shine that is in the lighting, or detail the render never had (hair strands, stubble): for those, re-render on a model with photographic texture.
Inputs
- The take at its delivery size: after
topaz-upscaleif it needs one, since grain size is in output pixels. - Its width, height and frame rate (
ffprobe-inspect-media). - The source shot it has to match, and a box on a face or a flat patch (a wall) for the
crop sheet, as
w:h:x:yat 1080p.
Run it
One command per rung. The noise is generated at half resolution (size 2) and scaled up, so each grain is 2 px wide. For a 1920×1080, 24 fps take at strength 10:
exec ffmpeg -hide_banner -loglevel error -y
-i <job>/rev1/shot03.mp4
-f lavfi -i "color=c=0x808080:s=960x540:r=24,format=yuv420p,noise=c0s=10:c0f=t+u,scale=1920:1080:flags=bicubic"
-filter_complex "[0:v]format=yuv420p[a];[a][1:v]blend=all_mode=grainmerge:shortest=1,format=yuv420p"
-c:v libx264 -crf 14 -preset slow -pix_fmt yuv420p -c:a aac -b:a 192k
-movflags +faststart
<job>/shot03_grain_ladder/shot03_size2_s10.mp4
- The noise source is mid grey (
0x808080), sograinmergeadds texture without moving brightness. s=is the take's size divided by the grain size (W/2 × H/2),r=is the take's frame rate, andscale=brings it back to W × H.c0s=is the strength;c0f=t+umakes it temporal (new every frame) and uniform.shortest=1ends the output with the take, so the frame count is unchanged.
Run the ladder at strengths 7, 10 and 14 (name the files <shot>_size2_s<strength>.mp4),
then a 100% crop sheet of one frame (frame 12 here) across the source, the take without
grain and every rung, left to right:
exec ffmpeg -v error -y
-i <source shot> -i <take> -i <…_s7.mp4> -i <…_s10.mp4> -i <…_s14.mp4>
-filter_complex "[0]select=eq(n\,12),crop=384:320:760:60[c0];[1]select=eq(n\,12),crop=384:320:760:60[c1];[2]select=eq(n\,12),crop=384:320:760:60[c2];[3]select=eq(n\,12),crop=384:320:760:60[c3];[4]select=eq(n\,12),crop=384:320:760:60[c4];[c0][c1][c2][c3][c4]hstack=5"
-frames:v 1 <job>/shot03_grain_ladder/shot03_compare_source_none_7_10_14.png
Look at the sheet, then put the sheet and the rungs on a review page next to the source and let the person pick.
What you get
One grained take per rung and a crop sheet. On the first job, strength 10 at size 2 was picked ("light") for a manager's face that the client called plastic and shiny; 7 is lighter, 14 heavier. Keep the ungrained take beside the picked one.
Tips and traps
- Size 2, not 1. Grain one pixel wide vanishes whenever the clip plays scaled down (a review grid, a phone), so the note comes back. Half-resolution noise survives.
- Judge at 100%. A review page shows clips scaled down, where every rung looks alike; the crop sheet is the evidence. Open the lightbox at full size for the moving clip.
- Grain does nothing for shine baked into the lighting. If the note is about shine, say so before spending time on a ladder.
Check it
- The frame count equals the take's (
exec ffmpeg -v error -i <out> -map 0:v -f framemd5 -, counting lines that don't start with#). - Measure the same flat patch or face on the source, the take and the pick with
python-measure-detail, and compare the numbers against the source's.


