CMKImageCompareEnableGate
Stop the Before/After Popping Up for a Module You Turned Off
- IMAGE A
- IMAGE B
- IMAGE A
- IMAGE B
Here's a bug class you don't think about until you hit it: you switch a module off, and ComfyUI still pops up its before/after comparison. The images are identical, because the module didn't run. It looks broken, it's confusing, and the only thing wrong is that a preview fired for a stage that was bypassed.
What it does
It's a two-image gate with a boolean. ENABLE is required; IMAGE A and IMAGE B are optional and lazy. When ENABLE is true, both images are required - if either is missing, you get CMK Image Compare Enable Gate requires both images when enabled - and both come back out unchanged as IMAGE A and IMAGE B.
When ENABLE is false, the node returns an ExecutionBlocker on both outputs.
That's the whole trick, and it's a better one than it looks. An ExecutionBlocker doesn't hand downstream a null - it stops that output from resolving at all. So the embedded comparer sitting behind this gate never gets its images, never executes, and never pushes its preview. No phantom comparison, no wasted work resolving images from a branch that isn't running.
The laziness matters for the same reason. check_lazy_status returns an empty list when the gate is disabled, meaning it doesn't even ask ComfyUI for the two images. On a disabled FaceProcess or Detailer module, that's real work not done: the images feeding the comparer come from expensive places, and requesting them purely to display them is exactly the kind of waste CMK's design notes call out.
Where it sits in a workflow
This is a developer node - DEV_ONLY in the source, and it exists as a plumbing detail inside CMK's published subgraphs rather than as something you'd add by hand. You'll meet it between a module's enable state and its comparison preview.
It's the same idea as the pack's other lazy gates - the family branch gates and the module bypass gates - applied to a much smaller question. Those decide whether a whole generation family runs; this one decides whether a preview runs. Both are built on the same principle: read a cheap signal first, keep everything expensive lazy behind it, and block hard when the answer is no.
Reach for the pattern, not the node, if you're building your own graph. The shape is: a boolean you already have, an early check_lazy_status that declines to resolve anything when it's off, and an ExecutionBlocker rather than a placeholder value.
Inputs and outputs at a glance
ENABLE (default true) is the switch. IMAGE A and IMAGE B are the pair you're guarding - in CMK's flows these are conventionally the processed result and the unprocessed source. Outputs IMAGE A and IMAGE B mirror the inputs, so the gate drops inline with no rewiring.
Note there's no third output carrying the boolean. If something downstream also needs to know whether the module ran, it reads that from the process pipe or from the module's own enable state, not from this node.
Install
Manager: search CMK Flow, install the CMKFlow entry, restart. Manually, from your ComfyUI root:
git clone https://github.com/CMKFlow/cmk_nodes.git custom_nodes/cmk_nodes
python3 custom_nodes/cmk_nodes/scripts/install_cmk_requirements.py
That second command is required after a manual clone - a git clone never runs pip on its own, and the pack pulls in ultralytics, onnxruntime, insightface, segment-anything, imageio-ffmpeg, nudenet and opencv-python. It finishes with CMK dependency check: OK when your environment is complete.
No model weights are involved for this node; the resource check is the pack-level step:
python3 custom_nodes/cmk_nodes/scripts/check_cmk_resources.py --models-root "/path/to/ComfyUI-Shared"
And the pack needs ComfyUI's Nodes 2.0 / Vue frontend setting on. CMK warns at startup if it isn't - lazy-graph behaviour like this is precisely what the dynamic rendering was built for.
The gotcha
ExecutionBlocker propagates. Anything downstream of this gate that expects an image will not get one when the gate is closed, so a consumer wired straight to these outputs has to be tolerant of a blocked input - which is why CMK pairs it with a comparer that is written to accept optional images in the first place.
If you wire it into someone else's workflow, that's the assumption to check before you blame the gate.
Inputs (3)
| Name | Type | Default | Description |
|---|---|---|---|
| ENABLE | BOOLEAN | true | — |
| IMAGE Aopt | IMAGE | — | |
| IMAGE Bopt | IMAGE | — |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| IMAGE A | IMAGE | — |
| IMAGE B | IMAGE | — |