Nodes/ComfyUI Model Bending/Apply Steering Vector
ComfyUI Node

Apply Steering Vector

Strength 2 is a nudge, strength 8 is a different picture

By abuzreq·Created 2 years ago·Updated 11 days ago· 26
Apply Steering Vector
  • model
  • steering_vector
  • MODEL
◄strength4.0►
◄t_start1.00►
◄t_end0.00►
◄apply_toboth►
◄normalizefalse►

You've built a direction - "colder", "more filmic", "more like run B" - and now you want it in an image. Apply Steering Vector is the other half of the pair: it takes a STEERING_VECTOR and adds it into the model at the layer the vector was measured at, scaled by a strength you pick.

The whole node is effectively one number. Get the number right and it feels like a slider for a concept that no prompt quite reaches.

How it works

The node clones the model patcher, resolves the vector's site on the diffusion model, and registers a hook on that layer. During sampling, the layer's output becomes:

output = output + strength * delta[step]

The delta is the per-step vector the steering node computed, broadcast to whatever shape the layer actually produces - per-channel means get a (1, ..., C, 1, 1)-style reshape, spatial means get interpolated to the live feature-map size if the resolution drifted. The add is cast to the layer's dtype and cached per (step, device, dtype, shape) so it isn't reconverted every call.

Two things make it behave better than it sounds:

  • Step alignment. If the run sampling now uses a different number of steps than the vector was measured with, the step index is remapped proportionally - 30-step vector, 20-step run, step 10 gets vector step 15.
  • Fail-closed time windows. With t_start/t_end set, the node needs to know the current diffusion time. If it can't resolve it (a model whose sampling object it can't read), it skips the steer for that pass and warns, rather than steering everywhere.

The inputs you'll actually touch

  • model - put this before every sampler you want steered. The output is a cloned MODEL; nothing upstream is mutated, so the same base model can feed a steered and an unsteered branch side by side.
  • steering_vector - from Steering Vector (from Activations).
  • strength - default 4, range −100 to 100, step 0.1. 0 turns it off entirely (the node short-circuits and returns a plain clone). Negative steers towards run B, which is how you get "summer" out of a "winter minus summer" vector.
  • t_start / t_end - the diffusion-time window, 1 = pure noise, 0 = clean image. Defaults 1 → 0, i.e. the whole run. The author's own tip is 0.8 → 0 to keep the composition and only let the steer touch the later part of the trajectory.
  • apply_to - both (default) or cond. cond only adds to the conditional half of the CFG batch; the unconditional pass is left alone with a per-sample weight of zero. Because guidance then extrapolates further away from the uncond result, adding to cond alone punches harder at the same strength. Worth knowing if you're on a model that still runs a real CFG pair. On a guidance-distilled model running at CFG 1 there's only the conditional pass anyway, so the setting does nothing.
  • normalize - off by default. When on, each step's delta is rescaled to the RMS of the activation it's being added to, so strength 1 means "one activation's worth of push" no matter how small the raw difference between your two prompts was. It makes strengths comparable across sites. It does not make them small.

Output is a single MODEL socket.

What number to use

From the shipped workflow's tips: strength ~2–3 is a clear push, 8+ breaks the image. The example itself runs 2.5. Treat 4 (the default) as already strong and reach for normalize before you reach for the top of the range. If you need to nudge it down and see nothing, the vector is probably the problem - check the relative_norm the steering node reported. Small relative norm, weak effect, at any strength.

Install

Manager → "ComfyUI Model Bending", or:

cd ComfyUI/custom_nodes
git clone https://github.com/abuzreq/ComfyUI-Model-Bending
pip install -r ComfyUI-Model-Bending/requirements.txt

kornia plus scikit-learn, then restart. Log lines carry the [model-bending] tag. There are no extra weights to download - a steering vector is computed live from two runs, which is also its main annoyance: nothing about it persists, so you re-run A and B every time you want to steer.

Where people get burned

  • A site that doesn't exist on this model. The vector's site is resolved strictly, so pointing an SD 1.5 vector at a Flux model fails loudly instead of silently doing nothing. Vectors aren't portable between architectures.
  • Shape mismatch. If the vector can't be broadcast onto the live activation - usually because you changed resolution or the model between measuring and applying - you get a warning that the vector was skipped. One warning, whole run unsteered.
  • Expecting it to survive a model swap. Measure on the checkpoint you're generating with. Same architecture, different fine-tune, and the direction is at best approximate.
  • Forgetting that 0 is a valid value. If you're A/B testing, set strength to 0 rather than bypassing the node - the graph stays identical and only the one variable moves, which is the debugging rule everywhere else in ComfyUI too.
Categorymodel_bending/probe

Inputs (7)

NameTypeDefaultDescription
modelMODEL—
steering_vectorSTEERING_VECTOR—
strengthFLOAT4.0-100–1000 = off; negative steers towards run B
t_startoptFLOAT1.000–1—
t_endoptFLOAT0.000–1—
apply_tooptCOMBObothboth CFG halves, or only the conditional one
normalizeoptBOOLEANfalseRescale the vector to the layer's activation size, so strength 1 = one activation's worth

Outputs (1)

NameTypeDescription
MODELMODEL—