Optional Image (obvpm)
An image path that only runs when you load one
- input
- image
- present
The most common ComfyUI workflow in the wild is the one that does two things and makes you pick: text-to-image when you have nothing loaded, image-to-image or an in-context edit when you do. Everyone builds it by bypassing half the graph by hand. Optional Image is the typed gate that makes the choice part of the graph instead of part of your memory.
What it does
input is an optional IMAGE. Wire something in and it comes back out of image unchanged. Leave it empty and on_empty decides what the rest of the workflow sees:
mute(default) - anExecutionBlockergoes downstream and every node on that path is silently skipped. The "generate from scratch" case, with the whole image branch gone.bypass-Noneis forwarded, and a downstream node with an optionalIMAGEinput treats it as unconnected. The "let the consumer decide" case.
The second output is present: a BOOLEAN, true when an image is wired in, and - this is the useful bit - still live in mute mode. So the flag survives even as the image path dies, which is what lets one question ("did a reference image get loaded this run?") drive a whole Lazy Switch.
Inputs and outputs
on_empty (enum, mute or bypass) and the optional input socket. Outputs: image and present. Nothing else to set.
Typical wiring: an image loader into input, image onward into whatever consumes the reference - a VAE encode, a ControlNet preprocessor, a resizer - and present into a Lazy Switch's boolean, with the reference-using branch on on_true and the plain text-to-image branch on on_false. Now both halves of the workflow live in one file and the graph picks the right one.
Why this is worth a node in 2026
The reference-image half of ComfyUI has changed shape, and it changed in a way that makes optionality more common, not less. The old stack was an adapter: IP-Adapter took a CLIP embedding from a reference and injected it. In the current generation the appearance work has moved to reference images consumed in-context by editing models - Flux 2's multi-reference composition, the R2V flows in video - and those paths are usable in a way the adapter route never was for a casual run. The practical consequence: people keep one workflow that does both jobs, and the reference is genuinely optional each time.
Typed gates are the sane way to express that. Where the wildcard version asks downstream to trust you, this one has a real IMAGE socket: an accidental latent or a stray model into it fails at the gate rather than halfway through an encode.
The trap that catches everyone
bypass and mute are for different graphs, and picking wrong is the usual misdiagnosis.
- With
bypass, a tolerant consumer copes. But "tolerant" means the consumer declared its image input optional. Arequiredimage input receivingNonestill errors - the message just shows up at the node consuming the gate instead of at the gate, which sends people hunting in the wrong place. - With
mute, downstream is skipped and cannot object or recover. Everything on that path stops. But nothing upstream ofinputwas saved: your loader already loaded, your preprocessor already preprocessed. Mute trims the tree below the gate, not above it.
And one thing neither mode does: stop an output node. A Save Image or Preview at the end of a graph is an execution root - ComfyUI works backwards from it - so if anything still feeds it, it runs. Cutting a branch off completely often needs a gate and a lazy node, and knowing which half you're missing is most of the debugging.
Install
Manager, search comfyui-obvpm, or by hand:
cd ComfyUI/custom_nodes
git clone https://github.com/chanon/comfyui-obvpm
Restart ComfyUI and you're done - no Python dependencies to install (the pack deliberately declares none; everything it imports already comes with ComfyUI) and no model files. All nodes carry an (obvpm) suffix, so typing obvpm into the node search menu finds every one of them.
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 | IMAGE | The value to pass through. May be left unconnected. |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | 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. |