Nodes/Nodes for use with real-time applications of ComfyUI/Similarity Filter πŸ•’πŸ…‘πŸ…£πŸ…
ComfyUI Node

Similarity Filter πŸ•’πŸ…‘πŸ…£πŸ…

Skip the Frames That Don't Matter and Get Your Frame Budget Back

By ryanontheinsideΒ·Created 2 years agoΒ·Updated about a year agoΒ· 82
Similarity Filter πŸ•’πŸ…‘πŸ…£πŸ…
  • image
  • image
  • should_execute
β—„always_executefalseβ–Ί
β—„threshold0.98β–Ί
β—„max_skip_frames10β–Ί

In a real-time loop, your expensive nodes - the sampler, the IP-Adapter pass, the upscaler - run on every frame whether the scene changed or not. For a talking-head demo where most of the frame is static, that's wasted compute. Similarity Filter exists to waste less of it: it compares each incoming frame to the previous one, and when they're basically identical, it says "skip" - outputting a False signal you can use to gate the heavy downstream work.

The comparison is cosine similarity between the current frame and the last one, computed on flattened tensors. Here's the honest part, because it matters: it doesn't do a hard pass/fail against your threshold. It uses the similarity to compute a probability of skipping - frames at or above the threshold almost always skip, frames far below almost always process, and there's a fuzzy zone in the middle where it's rolling dice. That's a deliberate design choice (it keeps a mostly-static stream from being perfectly regular in a way that looks odd), but it means this isn't a strict "same frame = skip" comparator. Occasionally a very similar frame slips through, and occasionally a slightly-different one gets skipped.

Two safety valves keep the fuzziness from biting you. max_skip_frames caps how many consecutive frames it can skip before it's forced to process one, so a scene change after a long static stretch doesn't stall your output indefinitely. And when it does decide to skip, it returns the previous frame (not the new one) with should_execute=False, so downstream never sees a gap - it just sees the same image again. That's the right behavior for gating.

The inputs that matter

  • threshold - similarity level above which frames count as "same." Default 0.98; higher = stricter = more skipping.
  • max_skip_frames - max consecutive skips before forced execution (default 10).
  • always_execute - note the difference from the rest of the pack: this defaults to off. Turn it on if you want the comparison to run on every execution regardless.

Outputs: image (the frame, or the previous one if skipping) and should_execute (BOOLEAN). Wire that boolean into a switch or gate.

Install & the reality check

In ryanontheinside/ComfyUI_RealtimeNodes - ComfyUI Manager (search "Control Nodes") or:

cd ComfyUI/custom_nodes
git clone https://github.com/ryanontheinside/ComfyUI_RealtimeNodes
cd ComfyUI_RealtimeNodes
pip install -r requirements.txt

The probabilistic behavior is the main thing people trip on - if you expected a deterministic comparator and your stream skips a frame it "shouldn't," that's the design, not a bug. Also beware the echo: because skipped frames output the previous image, chaining Similarity Filter before a node that itself feeds back into it can freeze a region of your output in a static state until max_skip_frames forces a refresh. Keep it upstream of your heavy sampler, feed its boolean into a gate, and you've bought yourself real headroom on a per-frame budget. It's one of the most practically useful nodes in the pack precisely because it's about not running things.

Categoryreal-time/control/utility

Inputs (4)

NameTypeDefaultDescription
imageIMAGEInput image to compare with previous frame
always_executeBOOLEANfalseβ€”
thresholdFLOAT0.980–1Similarity threshold (0-1). Higher values mean more frames are considered similar
max_skip_framesINT101–100Maximum number of consecutive frames to skip before forcing execution

Outputs (2)

NameTypeDescription
imageIMAGEβ€”
should_executeBOOLEANβ€”