JLC ControlNet Composition
Stack five ControlNets without the recursive slowdown
- positive
- negative
- CONDITIONING
- CONDITIONING
Here's the problem this node exists to solve: when you chain three or four ControlNets through the normal Apply nodes, ComfyUI links them recursively. Each one wraps the last via previous_controlnet, and the sampler has to walk that whole nested tree on every step. It works, but on ControlNet-heavy Flux workflows it gets slow and VRAM-hungry, and the more conditions you stack, the worse it compounds. JLC ControlNet Composition is the pack's answer: it takes that already-built recursive chain and flattens it into one composed ControlNet that evaluates each child independently and adds the results together.
This is the original cornerstone of the JLC pack (jlc-comfyui-nodes, by J. L. Córdova / Damkohler). The pitch is a linear cost model - roughly one ControlNet's worth of work per child instead of a nested explosion - and the author's own validation shows markedly better VRAM handling on multi-ControlNet Flux runs versus native recursive chains.
How it works
You don't build anything new here. You wire your chain exactly as you normally would - ControlNet Apply nodes feeding conditioning forward, one after another - then drop JLC ControlNet Composition in front of the sampler. On execution it:
- Traverses the
previous_controlnetchain from the conditioning, oldest to newest. - Shallow-copies each child and clears its
previous_controlnet, breaking the recursive linkage. - Replaces the whole chain in the conditioning with one composed wrapper that computes
Σ W_i · C_i(x)- each child evaluated against the same latent, then summed with your weights.
If the effective setup is exactly one ControlNet at weight 1.0, it skips the wrapper entirely and routes through a detached native single ControlNet - the mathematically equivalent fast path, so a single-control workflow costs you nothing.
The inputs that matter
positive/negative- your conditioning, already carrying the ControlNet chain from upstream Apply nodes. There's no image input here; the node only fuses what's already attached.slot_count- how many weight rows are active, 1–8 (default 5). This clips the chain: anything upstream beyondslot_countis ignored, and weight rows above it are too.weight_01…weight_08- each ControlNet's contribution. They go negative to -10, which is a real feature: negative weight on a condition pushes away from it.alpha- order bias, default 1.0. Below 1 favors the earlier ControlNets in the chain, above 1 favors the later ones. The math isW_i = w_i · alpha^i, so it's a gentle positional tilt, not per-slot control.
Both outputs are CONDITIONING (positive/negative) - wire them straight into your KSampler.
Installing it
Through ComfyUI Manager, search for jlc-comfyui-nodes, or:
cd ComfyUI/custom_nodes
git clone https://github.com/Damkohler/jlc-comfyui-nodes.git
Then restart ComfyUI. This node itself pulls in no extra dependencies - no model downloads, nothing. It just reshapes what's already on your graph.
Where people get burned
The pack's own troubleshooting is blunt about the biggest trap: if your multi-ControlNet run suddenly takes far longer than expected, it's usually not these nodes. Forced --lowvram on launch caused destructive partial unload/reload cycles and a catastrophic slowdown in the author's tested Flux workflows. Run normal VRAM mode (DynamicVRAM is fine) and only reach for --lowvram if you actually need it.
Second gotcha is bookkeeping, not breakage. If slot_count doesn't match the number of ControlNets actually upstream of you, the node logs warnings - weights set with no ControlNet to apply them to, or chain members silently clipped. Enable debug logging if you're chasing a mismatch; otherwise a silent "why is my fifth weight doing nothing?" is usually just slot_count sitting at 5 while only four Apply nodes are upstream.
Inputs (12)
| Name | Type | Default | Description |
|---|---|---|---|
| positive | CONDITIONING | — | |
| negative | CONDITIONING | — | |
| slot_countopt | INT | 51–8 | Number of visible/active ControlNet weight rows. Backend ignores weights above this count. |
| weight_01opt | FLOAT | 1.00-10–10 | Contribution of ControlNet 1 in extracted chain; can be negative. |
| weight_02opt | FLOAT | 1.00-10–10 | Contribution of ControlNet 2 in extracted chain; can be negative. |
| weight_03opt | FLOAT | 1.00-10–10 | Contribution of ControlNet 3 in extracted chain; can be negative. |
| weight_04opt | FLOAT | 1.00-10–10 | Contribution of ControlNet 4 in extracted chain; can be negative. |
| weight_05opt | FLOAT | 1.00-10–10 | Contribution of ControlNet 5 in extracted chain; can be negative. |
| weight_06opt | FLOAT | 1.00-10–10 | Contribution of ControlNet 6 in extracted chain; can be negative. |
| weight_07opt | FLOAT | 1.00-10–10 | Contribution of ControlNet 7 in extracted chain; can be negative. |
| weight_08opt | FLOAT | 1.00-10–10 | Contribution of ControlNet 8 in extracted chain; can be negative. |
| alphaopt | FLOAT | 1.000.01–2 | Order bias. <1 favors earlier ControlNets, >1 favors later ones. |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| CONDITIONING | CONDITIONING | — |
| CONDITIONING | CONDITIONING | — |