We set out with a simple goal: backport something from AV1 to H.264 that would provide compression improvements. Even minor gains would be worth documenting. What we got instead was a masterclass in why understanding where filters work matters more than how they work.
Spoiler: One experiment gave us a 366% increase in file size. Another gave us 82% reduction. Here's the story.
The CDEF Disaster: 366% File Size Increase
CDEF (Constrained Directional Enhancement Filter) is AV1's secret sauce for cleaning up compression artifacts. It's an in-loop filter that runs inside the encoder after quantization, smoothing out block boundaries and directional artifacts before those frames are used for motion prediction.
We thought: "What if we run CDEF as a preprocessor before encoding with H.264?"
❌ Catastrophic Failure
Test: Apply AV1 CDEF filter to The Matrix before H.264 encoding
Expected: Cleaner input = better compression
Result:
- File size: 309% to 366% LARGER
- Quality: Significantly degraded
- Conclusion: Total failure
Why CDEF Failed as a Preprocessor
CDEF is designed to work post-quantization, not pre-encoding. Here's what went wrong:
The In-Loop Filter Problem
AV1's filters work inside the encoding loop:
AV1 Encoding Flow:
┌──────────┐ ┌─────────────┐ ┌──────────┐ ┌──────────┐
│ Source │───>│ Quantization│───>│ CDEF │───>│ Wiener │
│ Frame │ │ (lossy) │ │ Filter │ │ Filter │
└──────────┘ └─────────────┘ └──────────┘ └──────────┘
│
↓
┌──────────────┐
│ Motion Pred │
│ (Next Frame) │
└──────────────┘
CDEF cleans up artifacts AFTER quantization damage has occurred.
Then cleaned frames are used for motion prediction.
Our Failed Approach:
┌──────────┐ ┌──────────┐ ┌──────────────┐
│ Source │───>│ CDEF │───>│ H.264 │
│ Frame │ │ Filter │ │ Encoder │
└──────────┘ └──────────┘ └──────────────┘
↑ │
└──────────── WRONG! ──────────────┘
CDEF was designed for post-quantization cleanup,
not pre-encoding spatial filtering.
Running CDEF before encoding:
- Removes high-frequency detail that H.264 could have compressed efficiently
- Introduces directional artifacts that H.264's motion prediction tries to encode
- Creates new patterns that weren't in the original, increasing bitrate
"Trying to use AV1's in-loop filters as preprocessors is like using a bandage before getting injured. The timing is everything." — WINK Engineering Team
The Pivot: Film Grain Removal
After CDEF's spectacular failure, we shifted focus. AV1's Film Grain Synthesis is different — it's about removing grain before encoding and storing parameters to re-add it during playback.
But we didn't want to port 3,500 lines of AV1 grain synthesis code. Instead, we tested a simpler question:
"Can we just denoise the video using existing FFmpeg filters and see what happens?"
Enter hqdn3d: The 20-Year-Old Champion
FFmpeg's hqdn3d (High-Quality Denoise 3D) has been around since 2003. It's a spatial/temporal denoiser that's perfect for removing film grain:
ffmpeg -i The_Matrix.mkv \
-vf "hqdn3d=4:3:6:4.5" \
-c:v libx264 -preset medium -crf 18 \
-tune film matrix_clean.mp4
Parameters:
luma_spatial=4 (moderate spatial smoothing)
chroma_spatial=3 (light spatial smoothing)
luma_tmp=6 (aggressive temporal denoising)
chroma_tmp=4.5 (moderate temporal denoising)
✅ Stunning Success
Test: Denoise The Matrix samples with hqdn3d before H.264 encoding
Result:
- Average reduction: 82% across three 5-minute samples
- Quality: Grain removed, detail preserved
- Processing time: 3-4x realtime (totally acceptable)
The Test Results: Matrix vs Bunny
We tested on two very different types of content:
Big Buck Bunny: Not So Lucky
Animated content is already very clean. Denoising it doesn't help much:
Why Big Buck Bunny Failed
Animated content is already:
- Clean: No film grain to remove
- Smooth: Large areas of flat color compress well
- Sharp: Denoising blurs intentional edges, increasing bitrate
Conclusion: This technique is content-specific. It works amazingly on grainy film, poorly on animation.
Visual Comparisons: The Complete Journey
Here's the full transformation for each sample: Original (with grain) → Denoised (grain removed) → With Grain Overlay (grain added back via Canvas). The grain overlay adds only 0.4 MB overhead!
Matrix @ 10 min - Dark Interrogation Scene
1. Original
730 MB
HEVC 10-bit with natural film grain
All that grain costs 607 MB!
2. Denoised
123 MB
Grain removed, detail preserved
Clean but looks "too digital"
3. With Grain Overlay
123.4 MB
Film aesthetic restored!
Best of both worlds ✓
Matrix @ 40 min - Agent Training Program
1. Original
677 MB
Training sequence with Morpheus
Consistent grain throughout
2. Denoised
126 MB
Smooth, compressed efficiently
3. With Grain Overlay
126.4 MB
Grain pattern composited
User-adjustable strength
Matrix @ 70 min - Subway Station Fight
1. Original
771 MB
High-motion action scene
Grain + motion = expensive!
2. Denoised
130 MB
Even action scenes compress well
3. With Grain Overlay
130.4 MB
Grain moves with animation
Authentic film look
Big Buck Bunny Comparisons (Animation)
For contrast, here's how animated content behaves — very different results!
Bunny @ 1 min - Original
16 MB
Already clean (animation)
Bunny @ 1 min - Denoised
14 MB
Minimal gains on animation
Bunny @ 1 min - With Grain
14.4 MB
Grain looks artificial on animation
Not recommended ✗
The Grain Overlay Solution
But wait — doesn't removing grain make the video look "too digital"? That's where the innovation comes in.
Instead of baking grain back into the video (which would destroy the compression), we:
- Store the clean denoised video (123 MB)
- Store a small tileable grain pattern (0.4 MB)
- Composite the grain during playback using HTML5 Canvas overlay
Total size: 123 MB + 0.4 MB = 123.4 MB (grain overhead is negligible!)
The Breakthrough
We created a browser-based video player that overlays film grain in real-time using the HTML5 Canvas API. It works in any modern browser with:
- ✅ Adjustable grain strength (slider control)
- ✅ Multiple grain patterns (synthetic or authentic Matrix grain)
- ✅ Toggle on/off during playback
- ✅ Animated grain for authentic film look
- ✅ Minimal CPU usage (5-10%)
How It Works: Canvas Overlay Technique
// 1. Load grain pattern (once, ~0.4 MB)
const grainImage = new Image();
grainImage.src = 'matrix_grain_sample.png';
// 2. On each frame, tile grain pattern across canvas
function drawGrain() {
ctx.globalAlpha = opacity; // User-adjustable
// Animate grain position for authentic look
const offsetX = (frame * speed) % 512;
const offsetY = (frame * speed * 0.7) % 512;
// Tile grain across video
for (let y = -512; y < height + 512; y += 512) {
for (let x = -512; x < width + 512; x += 512) {
ctx.drawImage(grainImage, x + offsetX, y + offsetY);
}
}
requestAnimationFrame(drawGrain);
}
🎬 Interactive Comparisons
Compare the original grainy footage with denoised versions. All videos are 5-minute samples from different timestamps.
Matrix @ 10 min - Original (730 MB)
Natural film grain visible throughout
Dark interrogation scene with Agent Smith
Matrix @ 10 min - Denoised (123 MB, -83%)
Grain removed, detail preserved
82% file size reduction
Matrix @ 40 min - Original (677 MB)
Agent training program sequence
Matrix @ 40 min - Denoised (126 MB, -81%)
Consistent compression across different scenes
Matrix @ 70 min - Original (771 MB)
Subway station fight scene (high motion)
Matrix @ 70 min - Denoised (130 MB, -83%)
Even action scenes compress well
Play denoised videos with adjustable film grain overlay
Lessons Learned
What We Discovered
- In-loop filters belong in-loop — CDEF, Wiener, and Loop Restoration are designed for post-quantization cleanup, not preprocessing
- Film grain removal works incredibly well — 82% average reduction on The Matrix across multiple samples
- Content matters — Animated content (Big Buck Bunny) saw minimal or negative gains
- Existing tools are powerful — FFmpeg's 20-year-old hqdn3d filter outperformed cutting-edge AV1 filter backporting
- Grain overlay is viable — HTML5 Canvas provides real-time grain compositing with negligible overhead
- Technique > Code — Understanding the approach matters more than porting thousands of lines of codec-specific code
When to Use This Technique
✅ Perfect for:
- Grainy film footage (shot on film stock)
- Surveillance video with sensor noise
- Old broadcast content with analog noise
- Low-light video with high ISO grain
❌ Don't use for:
- Animated content (already clean)
- Modern digital cinema (minimal grain)
- Content where grain is intentionally artistic
- Video already heavily compressed
The Math: Why This Works
Film grain is high-frequency random noise that's extremely expensive to encode:
Original The Matrix Frame:
├── Structural content (faces, objects, backgrounds)
│ → Compresses well with motion prediction
│ → ~100 KB per frame
│
└── Film grain (random per-pixel noise)
→ No correlation between frames
→ Motion prediction fails
→ ~500 KB per frame trying to encode noise
Remove the grain:
├── Structural content: ~100 KB per frame (same)
└── Film grain: REMOVED ✓
Result: 600 KB → 100 KB per frame = 83% reduction
"Compression is about finding patterns. Film grain has no patterns. Stop trying to compress it — store it once and overlay it." — WINK Engineering
Comparison to AV1 Film Grain Synthesis
AV1's approach vs. our grain overlay technique:
AV1 Film Grain Synthesis
- Store parametric grain data per frame
- Synthesize grain in AV1 decoder
- Requires 3,500+ lines of code
- Works only in AV1 decoders
- Frame-accurate grain
- Not user-adjustable
Result: Codec-specific, complex
Our Grain Overlay Method
- Store single tileable grain pattern
- Composite in browser using Canvas
- ~150 lines of JavaScript
- Works in any browser
- Approximate (tiled) grain
- ✅ User-adjustable strength
Result: Universal, simple, flexible
Distribution Strategy
For end users, you can package the optimized content like this:
Matrix_Optimized/
├── matrix_clean.mp4 (123 MB - denoised video)
├── grain_overlay.png (0.4 MB - tileable grain pattern)
├── player.html (HTML5 player with grain controls)
└── README.txt (instructions)
Total: 123.4 MB vs 730 MB original (83% reduction)
Users can:
- Play in any video player (clean version)
- Use HTML player for grain overlay + adjustable controls
- Upload to web hosting for streaming
- Share via standard video platforms
Conclusion: The Right Tool for the Right Job
We set out to backport AV1 features to H.264. The CDEF experiment failed spectacularly — a 366% file size increase taught us that in-loop filters don't work as preprocessors.
But the film grain removal experiment succeeded beyond expectations — an 82% average reduction on The Matrix proved that sometimes the simplest approach (use existing denoising tools) beats complex codec-specific implementations.
Key Takeaways
- Don't backport features without understanding where they work in the pipeline
- Existing tools (like FFmpeg's hqdn3d) can be more effective than cutting-edge codec features
- Film grain removal is incredibly effective on appropriate content (grainy film footage)
- Grain overlay via HTML5 Canvas provides the aesthetic without the file size penalty
- Content-specific techniques often outperform one-size-fits-all codec improvements