Optional Video (obvpm)
A video socket that's allowed to be empty
- input
- video
- present
The problem it solves
Most workflows are written as if every input will be there. Video makes that a bad assumption: a clip is a fat tensor, sampling it takes minutes, and the tail end - upscale, interpolate, save - is the part you'd rather not run when there's nothing to run it on.
The usual wish is one graph that does several jobs: image-to-video when you only have a still, video-to-video when you drop a clip in, no reference at all while you're testing a prompt. ComfyUI's engine has a rule for this - an unwired optional input isn't passed to the node, so the node sees None - but only inside a node that declared the input optional. Everything downstream still expects a video and errors without one.
This node lets absence be a decision you make on purpose instead of an error you chase.
What it actually does
Two behaviours, one dropdown, and the default is the one that bites.
With something connected, the gate is invisible: video through, present true, nothing else happens.
When nothing is connected, on_empty decides. mute (the default) makes the value output emit ComfyUI's ExecutionBlocker, and every node downstream is silently skipped - no exception, no red node, the queue just finishes and that work never happened. That's the mode for a save-video tail or an upscale branch you want dead when there's no clip. bypass outputs None, the "nothing was wired here" value, which a downstream node with an optional video input reads as unconnected and handles itself. Feed None into a required video input and you get an error, so bypass is only safe when you know the consumer is tolerant.
Then there's present, the genuinely clever bit. In mute mode the value path is blocked but the boolean isn't - it still comes out as a real False rather than an ExecutionBlocker, so downstream logic keeps running while the video path stays dead. That's what lets you say "no clip? take the fallback branch" instead of "no clip? nothing at all." One nuance the tooltip glosses over: present follows the value, not the socket, so if this gate is fed by another gate sitting in bypass and passing None, present reads false too.
Inputs, outputs, and what to wire where
There isn't much to learn, which is the point. input is optional and typed VIDEO - core's VIDEO object, not a folder of PNGs. on_empty is the only widget you set. Outputs are video (the passthrough, blocked or None when empty) and present (the is-there-a-clip flag).
Wire video into whatever consumes video: the next gate, a create-video step, Save Video, an audio-mux node. Wire present into a switch's boolean - the pack's own Lazy Switch is the natural partner, since the unselected branch never executes and the fallback costs nothing while the main path is live. The other standard move is present through a Boolean invert into a Mute node.
Worth forming the habit: this gates the downstream work. Whatever feeds the gate already ran - if the clip comes out of a sampler, that sampler paid for the frames before the block applied. Conditional upstream work needs laziness, not a blocker.
Installing it
Manager: search obvpm and install the pack - every node in it carries the (obvpm) suffix so one search finds them all.
By hand:
cd ComfyUI/custom_nodes
git clone https://github.com/chanon/comfyui-obvpm
Then restart ComfyUI. No Python dependencies, no model files, no pip install - deliberate: the pack's pyproject.toml keeps dependencies empty so its pins can never contradict ComfyUI's own on torch/numpy/av.
Two things you'll notice after the restart. The node shows up as Optional Video (obvpm) under obvpm/gates, so don't hunt for "gate". And there's a new beta57 scheduler in every scheduler dropdown - the pack registers RES4LYF's beta schedule (alpha=0.5, beta=0.7) at import. Harmless, occasionally useful, surprising if nobody warned you.
The repo moved from obvpm/comfyui-obvpm to chanon/comfyui-obvpm. The old URL redirects, so it doesn't matter much either way.
Where people get burned
The standard complaint on any mute-style node is "the run finished and nothing happened." With on_empty defaulting to mute, dropping this node in and leaving the input empty is a working configuration - it just silently kills the branch. Prototyping? Set bypass.
Second, bypassing into a required socket: the error names the downstream node, not the gate, which sends people reading the wrong part of the graph. Scroll up a node.
Third, type mismatch. This socket takes core's VIDEO, so AnimateDiff-era and VideoHelperSuite graphs - which pass frames as an IMAGE batch plus an info dict - won't connect to it. Use Optional Image for those, or Optional Any for a mixed bag; a LATENT path wants Optional Latent.
And know the tell: a muted run looks successful. No exception, no warning, just a shorter execution and no output. When a graph behaves like half of it evaporated, check on_empty first.
Inputs (2)
| Name | Type | Default | Description |
|---|---|---|---|
| on_empty | COMBO | mute | What downstream sees when no input is connected: mute skips every downstream node, bypass outputs None. |
| inputopt | VIDEO | The value to pass through. May be left unconnected. |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| video | VIDEO | The input passed through. When the input is empty: blocked (mute) or None (bypass). |
| present | BOOLEAN | True when an input is connected. Stays live even in mute mode. |