Layer Steer
Steer only the transformer layers where your concept actually lives
- model
- MODEL
Concept Steer adds its direction to the conditioning, which the model then projects through every cross-attention layer identically. Layer Steer is the surgical version: it hooks into the output of each cross-attention block and injects a per-layer-scaled concept direction. That buys you three things the docstring spells out - interpretability (which layers actually respond to your concept?), precision (only steer where the concept manifests), and fewer artifacts (don't push a concept into layers that don't "understand" it, which causes incoherence).
How it works
It uses ComfyUI's set_model_attn2_output_patch() to intercept each cross-attention block's output and add a scaled concept direction before the residual stream continues. The per-block strength comes from a layer mask - a compact string format like 0-5:0.2, 6-18:1.0, 19-23:0.5 - or from a preset (all, early, middle, late, custom) computed over num_layers.
The presets encode the community heuristic about what each band of the network handles: early layers = composition/layout, middle = subject/object features, late = texture/detail/lighting. So "moody lighting" is a late-layer concern and "minimalist composition" is an early one; Layer Steer lets you steer precisely where each lives instead of everywhere at once.
Bonus: report_stats prints per-layer steering statistics to the console every report_interval denoising steps. That's the interpretability payoff - you can literally watch which layers are most affected by your concept injection and learn where the concept "lives" in the model.
The inputs that matter
- model - a MODEL; you get a patched MODEL back for the KSampler.
- lens - dropdown (or custom_lens_path).
- strength - global strength, multiplied by the per-layer mask.
- layer_preset -
all/early/middle/late/custom. - layer_mask - used when preset is
custom. Format:range:strength, range:strength, ....6-18means layers 6–18 at 1.0;10,11,12:1.5targets individual layers; unspecified layers default to 0.0. - num_layers - total transformer layers, for preset math. 48 for Lumina2/Z Image Turbo, 24 for SD 1.5, 70 for SDXL. This is the one input beginners get wrong.
- normalize - scale the direction relative to cross-attention output norms; leave on.
- report_stats / report_interval - console diagnostics.
Output is a single MODEL. Wire [Load Model] → Layer Steer → KSampler.
Installing
Pack-standard: ComfyUI Manager search "Concept Steer", or git clone https://github.com/Nynxz/ComfyUI-ConceptSteer into custom_nodes/ and restart. Same as the rest of the pack - a trained lens, transformers/safetensors for training, no new model downloads.
Common gotchas
num_layerswrong = presets in the wrong place. The presets divide the layer count into thirds, so a wrong count silently steers the wrong band. Check the model's real depth (48 for Z Image Turbo, 24 for SD 1.5, 70 for SDXL per the tooltip).- Layer mask syntax is exact. Ranges with
:strength, commas between entries, layers you don't mention get 0.0 - a stray space or missing colon turns the whole mask into noise. - Use the patched MODEL. Same trap as Timestep Steer - the output of this node is the model to sample with; wiring the original loader model to the KSampler gives you nothing.
- Report stats are console output, not a node output. If you're looking for them in the graph, you won't find them. They print to the ComfyUI terminal.
Inputs (10)
| Name | Type | Default | Description |
|---|---|---|---|
| model | MODEL | — | |
| lens | COMBO | None | Select a concept lens to apply |
| strength | FLOAT | 1.00-10–10 | Global steering strength (multiplied by per-layer mask) |
| layer_preset | COMBO | all | Which layers to steer: • all — every layer at full strength • early — first third of layers (composition/layout) • middle — middle third (subject/object features) • late — last third (texture/detail/lighting) • custom — use layer_mask input for fine control |
| layer_mask | STRING | 0-5:0.2, 6-18:1.0, 19-23:0.5 | Fine-grained per-layer strength (only used when preset='custom'). Format: 'range:strength, range:strength, ...' Examples: '6-18' → layers 6-18 at 1.0 '0-5:0.2, 6-18:1.0, 19-23:0.5' → graduated '10,11,12:1.5' → individual layers Layers not specified default to 0.0 (no steering). |
| num_layers | INT | 481–200 | Total number of transformer layers in the model. Used for preset calculations. 48 for Lumina2/Z Image Turbo, 24 for SD 1.5, 70 for SDXL. |
| normalize | BOOLEAN | true | Scale direction relative to cross-attention output norms |
| report_stats | BOOLEAN | true | Print per-layer steering statistics to console. Shows which layers are most affected by the concept injection — useful for understanding where concepts 'live' in the model. |
| report_interval | INT | 51–100 | Print layer report every N denoising steps |
| custom_lens_path | STRING | Override: absolute path to a .pt lens file |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| MODEL | MODEL | — |