CLIP Text Encode (Perp-Weight)
Prompt Weights Past 1.5 Usually Melt Your Image. This CLIP Encoder Gives Them Room.
- clip
- CONDITIONING
You know the drill: (detailed face:1.4) works, (detailed face:2.0) turns your image into an overcooked HDR mess. Normal prompt weighting just scales the token vector, and outside a narrow band the scale blows the embedding up instead of emphasizing it. CLIP Text Encode (Perp-Weight) is a drop-in replacement for ComfyUI's stock CLIP Text Encode that redoes that weighting math, so weights can run much wider - (word:2.5), even negative weights - before the image gives up.
It comes from bvhari, the same author as the PerpNegGuider node people reached for on Flux, and it's the same idea one level down. Perp-Neg (a 2023 paper) is about negative prompts: subtract only the part of the negative vector perpendicular to the positive prompt, and you remove "bad hands" without also deleting "hands". This node applies that perpendicular decomposition to individual tokens instead of whole prompts. The README's whole pitch is one line: "Allows a wider range of values for the weight."
What it actually does
ComfyUI's vanilla (word:1.3) weighting scales the token's vector by the weight before encoding; crank it and the vector gets huge, and huge means distortion. This node flips the order. It encodes your prompt with every weight pinned to 1.0 - plus an empty-prompt encoding as a reference - and then edits the output vectors afterward, one token position at a time. Above 1.0 it grows the token vector along the direction where the empty prompt's vector overlaps it, which is a gentler ramp than straight multiplication, so bigger numbers stay usable. Sub-1 weights interpolate toward the empty vector; negative weights reflect through it.
If you're wondering how it knows your token positions when Comfy chunks long prompts at 77 tokens: it doesn't have to - it works per position inside the chunk, so long prompts are handled. It also auto-detects whether your CLIP is SDXL (two encoders, CLIP-L and OpenCLIP-G, handled separately) or a single-encoder model like SD 1.5. If this all sounds like an opinionated way to do attention, it is - that's the point.
The inputs that matter
There are exactly two, and they're the same two the vanilla node takes:
- text - your prompt, using the ordinary
(word:1.3)syntax. This is where the wider range pays off: pushes past 2.0 and real negative weights are the whole reason to be here. - clip - the CLIP model from your checkpoint loader (SD 1.5, SDXL, Illustrious, NoobAI, Pony).
Output is a single CONDITIONING, wired into the sampler exactly like any other text encode. One quirk worth knowing: the pooled output it passes along comes from the unweighted encoding, so for SDXL don't expect weighting to ride through in the pooled branch - it's the token-level conditioning that carries the change.
Installing it
Trivial - one small Python file, no extra dependencies, no model downloads. In ComfyUI Manager, search ComfyUI_PerpWeight (or its display name "Perp-Weight") and install. Or by hand:
cd ComfyUI/custom_nodes
git clone https://github.com/bvhari/ComfyUI_PerpWeight
Restart ComfyUI and you'll find CLIP Text Encode (Perp-Weight) under the "conditioning" menu.
The catch, and what it's not for
Two honest caveats. First, this is CLIP-only, so it works on SD 1.5 and the SDXL lineage (Illustrious, NoobAI, Pony) and nowhere else. On Flux, SD3, or anything with an LLM text encoder, prompt weighting syntax doesn't work at all - that's the disable_weights=True path modern encoders take - and this node won't rescue you; you'd be feeding literal ((parentheses)) into a chat prompt. Second, this is a quiet, experimental repo with basically no community footprint: no issue churn, no tutorials, single-digit impressions. You're the test subject, and that's fine for a node this cheap to install.
If it seems to do nothing, check your expectations, not your wiring: at small weights it behaves near-identically to vanilla, because the perpendicular path only diverges at the extremes. Use it when a (word:1.6) isn't landing and you'd rather push harder than stack more parentheses - that's the one situation where it genuinely earns its place in the graph.
Inputs (2)
| Name | Type | Default | Description |
|---|---|---|---|
| text | STRING | — | |
| clip | CLIP | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| CONDITIONING | CONDITIONING | — |