Shader Overlay (FFMPEGA)
70 real-time GLSL effects on your video, no LLM involved
- images
- mask
- images
- video_path
- mask
Most of FFMPEGA is wrapped around an LLM agent that turns plain English into ffmpeg filters. Shader Overlay isn't. It's a standalone node: hand it frames, pick an effect, get shaded frames back. No model prompting, no agent, no API keys, and it runs fast enough that you can iterate. If you just want your clip to look like VHS or a CRT monitor, this is the shortest path in the pack.
How it works
The effects are GLSL shaders - 70 of them ship in the pack's shaders/ directory, everything from crt, vhs, holographic and glitch through weirder stuff like voronoi, water_ripple, night_vision, force_field, matrix, stained_glass, pixel_sort and singularity. They're applied through FFmpeg's libplacebo filter, which compiles and runs them on the GPU over Vulkan.
That's the mechanism, and it's also the caveat: libplacebo has to be compiled into your FFmpeg build, along with Vulkan and glslang. Plenty of distro and package-manager ffmpeg builds don't include it. The node detects this at import and, if libplacebo isn't there, falls back to plain FFmpeg filter approximations for each preset. The fallbacks are hand-written to be recognizable - vhs becomes noise plus a colour-balance shift and a saturation cut, glitch becomes an rgbashift - but they are not pixel-accurate versions of the shader. If you've seen screenshots of the shader and your output looks like a discount version of it, that's why.
What you actually set
preset is the main dial, and it's a long dropdown with emoji-named category dividers in it - ignore those, they're labels, not effects. The 🎲 random entry picks one for you, which is a better way to browse 70 shaders than scrolling.
intensity- 0 to 1, blending the shaded result with the original. At partial values the shader output is blended with your source frame usingblend_mode(normal,addition,multiply,screen,overlay,softlight). Full-strength glitch on a talking-head shot is unwatchable; 0.3 is a look.preset_2/preset_3with their ownintensity_2/intensity_3- up to three stacked layers. This is where it gets fun, and also where it gets muddy. Two is usually plenty.speed,phase,hue_shift- animation speed multiplier, a phase offset in seconds so the effect starts at a different point in time, and a post-shader hue rotation in degrees.resolution_scale- 0.25 / 0.5 / 1.0 / 2.0. Half-res is a real speedup on long clips and barely visible for noisy effects like glitch or film grain.custom_shader_path- point this at your own.glslfile and it overrides the preset. It has to be a valid mpv/libplacebo HOOK-format shader.
The two inputs that turn this from a filter into something smarter are mask_target and enable_vda / enable_normals. mask_target is a SAM3 text prompt: name an object and the shader only applies there - which is how you put the scanline look on the character and leave the background clean. invert_mask flips that to everything except the target. The depth switches pull in Video Depth Anything (vits ~102 MB, vitb ~390 MB, vitl ~670 MB) and NormalCrafter surface normals, so depth-native shaders and depth-modulated intensity have real per-pixel data to work with; depth_strength scales how hard depth modulates the effect, and depth_input / normals_input let you supply your own maps and skip inference entirely.
Outputs and install
Outputs are images, video_path, and a mask pass-through (it forwards whatever you wire in, unchanged). Installing is the standard pack route:
cd ComfyUI/custom_nodes
git clone https://github.com/AEmotionStudio/ComfyUI-FFMPEGA.git
pip install -r ComfyUI-FFMPEGA/requirements.txt
or ComfyUI Manager → search ComfyUI-FFMPEGA. Restart after. FFMPEG on PATH is mandatory; the libplacebo/Vulkan part is the quality upgrade, not the entry condition.
Where people get burned
"It doesn't look like the demo." Almost always the libplacebo fallback. Check whether your ffmpeg has it compiled in - if it doesn't, either get a build that does or accept the approximation. There's no in-node error for this; it just quietly gives you the lesser version.
Stacking three heavy shaders at 2.0× resolution scale on a 1080p clip is a good way to make a GPU-bound node feel like a diffusion model. Start at 1.0, drop to 0.5 if you're iterating.
Depth and normals are model downloads. Enabling them on a whim means a first-run fetch and a slower queue. If you've already got a depth map, depth_input is the cheap path.
Inputs (25)
| Name | Type | Default | Description |
|---|---|---|---|
| preset | COMBO | none | Built-in shader preset to apply. Select 🎲 random for a surprise. |
| intensity | FLOAT | 1.000–1 | Blend intensity between original (0.0) and fully shaded (1.0). At partial intensity, the shader output is blended with the original frame. |
| imagesopt | IMAGE | Video frames as IMAGE tensor [N,H,W,3]. | |
| video_pathopt | STRING | Path to a video file on disk. | |
| custom_shader_pathopt | STRING | Path to a custom .glsl shader file. When set, overrides the preset selection. Must be a valid mpv/libplacebo HOOK-format GLSL file. | |
| preset_2opt | COMBO | none | Second stacked shader layer. |
| intensity_2opt | FLOAT | 1.000–1 | Intensity for the second shader layer. |
| preset_3opt | COMBO | none | Third stacked shader layer. |
| intensity_3opt | FLOAT | 1.000–1 | Intensity for the third shader layer. |
| speedopt | FLOAT | 1.00.1–5 | Animation speed multiplier (1.0 = normal). |
| hue_shiftopt | FLOAT | 00–360 | Post-shader hue rotation in degrees. |
| blend_modeopt | COMBO | normal | Blend mode when intensity < 1.0. Controls how the shader mixes with the original. |
| phaseopt | FLOAT | 0.00–10 | Animation phase offset in seconds. Starts the effect at a different point in time. |
| shader_paramsopt | STRING | JSON parameter overrides for the shader. Reserved for future per-shader customization. | |
| resolution_scaleopt | COMBO | 1.0 | Shader processing resolution scale. 0.5 = half res (faster), 2.0 = double (quality). |
| mask_targetopt | STRING | SAM3 text prompt — when set, the shader is applied only to the detected object region. Leave empty to apply the shader to the entire frame. | |
| invert_maskopt | BOOLEAN | false | When ON, applies the shader to everything EXCEPT the masked target (i.e. the background). |
| enable_vdaopt | BOOLEAN | false | Enable Video Depth Anything depth estimation. Provides per-pixel depth for depth-native shaders and post-composite depth masking for regular shaders. |
| enable_normalsopt | BOOLEAN | false | Enable NormalCrafter surface normal estimation. Provides per-pixel surface normals for enhanced lighting, rim effects, and emboss in depth-native shaders. |
| depth_encoderopt | COMBO | vits | VDA model variant. vits=fast (~102 MB), vitb=balanced (~390 MB), vitl=highest quality (~670 MB). |
| depth_inputopt | STRING | External depth map video path. When provided, skips VDA inference and uses this depth map directly. | |
| normals_inputopt | STRING | External normal map video path. When provided, skips NormalCrafter inference and uses this directly. | |
| depth_strengthopt | FLOAT | 1.000–1 | How strongly depth modulates shader intensity (0 = no depth effect, 1 = full depth modulation). |
| fpsopt | FLOAT | 24.001–120 | Frame rate for IMAGE tensor input encoding. Ignored when video_path is used. |
| maskopt | MASK | Optional upstream MASK pass-through. Forwarded as-is to the mask output. |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| images | IMAGE | — |
| video_path | STRING | — |
| mask | MASK | — |