Nodes/SAX Bridge/SAX Structure Lock (SDXL)
ComfyUI Node

SAX Structure Lock (SDXL)

Stop SDXL From Melting Your Torso on the Second Pass

By so16tm·Created 7 months ago·Updated a day ago· 2
SAX Structure Lock (SDXL)
  • pipe
  • PIPE
◄controlnet_nameNone►
◄modetile►
◄strength0.60►
◄start_percent0.00►
◄end_percent1.00►

The failure this fixes

You generate a decent SDXL image. Then you run it through a detailer or push it up a scale factor, and the second pass quietly re-rolls the parts that were already right: the torso anatomy drifts, an arm fuses into a sleeve, the composition slides. A full-image img2img pass at partial denoise has nothing anchoring the layout, so "add detail" turns into "generate a similar image."

SAX Structure Lock (SDXL) is a one-input node that drops a ControlNet structure constraint onto the pipe, so the pack's Detailer and Upscaler passes get a scaffold to hold on to. It's the plumbing answer to a classic fix - ControlNet on your redraw pass - without you building the preprocessor branch by hand.

It's a carrier, not a loader

This is the part worth understanding, because it's why the node looks empty. Structure Lock does not load a ControlNet. It writes a small settings block into PIPE: which checkpoint, which mode, how strong, and over what slice of the sampling steps. The consumers - SAX Detailer, SAX Enhanced Detailer, SAX Upscaler - read that block and, per pass, generate the structure hint internally from the image they're already working on. The detailer hints from its cropped region; the upscaler hints from the upscaled full frame. The ControlNet is applied locally to conditioning inside each consuming node and is never written back to the pipe, so it can't get applied twice.

Two consequences fall straight out of that design:

  • Placing the node enables the constraint. Removing it disables it completely. There's no enabled toggle and no off mode - the pack's author is explicit that he refuses to ship "visible but inert" UI. If you want it off, delete the node.
  • It only affects those img2img passes - nothing happens to your base generation. If anatomy breaks on the first pass, this isn't the tool; fix it at native resolution, because upscaling bakes defects in.

The inputs that matter

  • controlnet_name - a ControlNet from models/controlnet. Use a Union model; xinsir's Union (the "promax" one) is the SDXL standard and covers tile, depth, pose and lineart in a single file. This is also why tile mode works at all: SDXL never got a standalone Tile ControlNet, so Union's tile condition is the route in.
  • mode - tile (default) is a blurred-structure lock and needs zero extra dependencies: the hint is built by downscaling to half, restoring the size and applying a light gaussian blur. depth, openpose and lineart use real detectors and need controlnet-aux.
  • strength - 0.6 is the balanced default. Higher locks structure harder; at the top of the range you've told the redraw to change almost nothing, which looks exactly like "the detailer stopped working."
  • start_percent / end_percent - optional, 0.0 to 1.0, for limiting the constraint to part of the sampling run. They're validated so that an empty range (start ≥ end) is rejected instead of silently doing nothing.

Output is a single PIPE. Slot it between the Loader and the Detailer/Upscaler in the chain.

Note the fail-fast behaviour: running with controlnet_name = None is an error, not a no-op. A ControlNet that can't be loaded is an error too. The author's stated logic is that a placed node is an explicit intent, so it should shout rather than pretend.

Installing it

cd ComfyUI/custom_nodes
git clone https://github.com/so16tm/SAX_Bridge
# restart ComfyUI

ComfyUI Manager → install via Git URL with the same address. Then put a Union ControlNet in ComfyUI/models/controlnet/. If you want depth, openpose or lineart instead of the dependency-free tile:

pip install controlnet-aux

That one is worth flagging: the pack README's dependency table doesn't mention controlnet-aux, but the source imports it for every detector mode, and the first run downloads annotator weights (MiDaS for depth, DWPose for pose, LineartAnime for lines). Detectors are cached per process, and a failed one isn't retried.

Where people get burned

  • controlnet_name is 'None' / ControlNet 'x' was not found in the controlnet folder. Either you placed the node without picking a model, or the file isn't where ComfyUI expects. Both are placement-time errors, so you find out immediately.
  • openpose (or depth, or lineart) behaving like tile. That's the fallback chain doing its job: a missing controlnet-aux, an unavailable detector, or a detector that found nothing (a blank pose map is treated as a failure) degrades down - openpose → depth → tile, lineart → tile. Check the console for the warning; the image won't tell you.
  • Cranking strength to 1.2+ and calling it dead. Strong structure constraint plus partial denoise means the pass has almost no room to improve anything. 0.6 first, and tune from there.
  • Expecting it on a modern DiT base. The pack is upfront that this is the SDXL path - the constraint isn't wired up for its Krea 2 / DiT loaders, and 2026-era bases get a narrower ControlNet menu anyway, so don't assume the trick ports.
CategorySAX/Bridge/Control

Inputs (6)

NameTypeDefaultDescription
pipePIPE_LINE—
controlnet_nameCOMBONoneControlNet model for the structure constraint. Union ControlNet (SDXL) is recommended.
modeCOMBOtileStructure hint type. tile=blurred-structure lock (no extra deps), depth=depth lock, openpose=pose skeleton lock, lineart=line art lock. depth/openpose/lineart need controlnet_aux and fall back to tile when unavailable.
strengthFLOAT0.600–1.5Structure constraint strength. 0.6=balanced. Higher values lock structure more strongly.
start_percentoptFLOAT0.000–1Sampling step range start for the ControlNet (0.0=first step).
end_percentoptFLOAT1.000–1Sampling step range end for the ControlNet (1.0=last step).

Outputs (1)

NameTypeDescription
PIPEPIPE_LINE—