Optional Latent (obvpm)
Optional latents for img2img, inpainting and hires passes
- input
- latent
- present
Latents are the shape of every "reuse what I already have" decision in ComfyUI. An img2img pass starts from an encoded latent; an inpaint pass starts from a latent with a mask baked in; a hires-fix or a second sampler pass starts from the first sampler's latent. Every one of those is a place where the latent is sometimes there - and a KSampler's latent_image is a required input, which is exactly why so many tutorials tell you to keep dummy nodes around.
Optional Latent (obvpm) is the gate for that. Plug a latent in and it goes through. Leave it empty and the gate decides what happens next, instead of the graph refusing to queue.
What on_empty does
mute(default) - anExecutionBlockergoes out of thelatentsocket and every downstream node is silently skipped. Nothing downstream can catch it, recover from it, or report it. It's a kill switch for a whole path.bypass-Noneis forwarded. A downstream node with an optional LATENT input treats it as unconnected and does its own thing; a required socket errors. Sobypassis the mode to use when you want the consumer to decide, andmuteis the mode for "this branch does not exist today".
Neither mode touches the upstream half. Whatever produced the latent has already run by the time the gate can object - for a cheap VAE Encode that's nothing, for a whole sampler stage it's the expensive part, and that's an argument for putting a Lazy Switch in front of the stage rather than gating its output.
Inputs and outputs
input- optional LATENT, may be left unconnected.on_empty-muteorbypass.latent- the input passed through, or blocked /None.present- BOOLEAN, true when the input is connected, and it stays live even in mute mode so it can drive branch logic while the value path is dead.
That present output is the useful one. Wire it into a Lazy Switch's boolean and you get a graph that skips the upstream work of the branch it isn't taking, not just the downstream nodes after the gate. Gates handle downstream, laziness handles upstream, and you need both to switch a branch off completely.
Where it shows up in real graphs
Hires-fix as an option. First KSampler's latent goes to the gate. Nothing connected → the gate mutes the upscale branch and your workflow is a plain single-pass render. Connect it → the second pass runs. Same graph, one wire.
Inpaint that becomes img2img. An inpainted latent (from VAE Encode For Inpaint) plumbed in makes the mask pass the live path; unplug it and the graph generates fresh. Because the gate sits on the latent rather than the image, you skip the whole encode when the branch is off.
Two-stage refinement. A refinement pass that only makes sense when you actually sampled with the matching model - the latent gate is where you say so, and present is what tells the rest of the graph which mode it's in.
Install
ComfyUI Manager → search comfyui-obvpm, or:
cd ComfyUI/custom_nodes
git clone https://github.com/chanon/comfyui-obvpm
Restart ComfyUI. Category obvpm/gates. No extra Python dependencies and no model files - the pack adds none. Every id carries the (obvpm) suffix since 0.2.0, after a class-id collision with another pack, so obvpm in the node search is the fast way to see the full set; older workflows are migrated automatically when opened.
Gotchas
The classic one is on the Sampler side, not here: if you convert latent_image on a KSampler from a widget to an input and then feed it a gate in bypass mode, the sampler sees None - and latent_image is required, so it errors. That's the "mute or nothing" rule for required sockets, and it's the reason so many conditional-latent workflows end up wrapping the entire sampler stage in a Lazy Switch instead of gating the latent going into it.
Second: a muted latent path means an entire decode and save can vanish without a red error anywhere. If your final image just doesn't appear, check gates before sampler settings. Silent is the point; silently confusing is the tax.
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 | LATENT | The value to pass through. May be left unconnected. |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| latent | LATENT | 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. |