Nodes/ComfyUI-FunPack/FunPack Negative Erase
ComfyUI Node

FunPack Negative Erase

Giving the negative prompt a job when you're sampling at CFG 1

By digital-garbage·Created about a year ago·Updated about 14 hours ago· 20
FunPack Negative Erase
  • positive
  • negative
  • positive
  • status
◄strength0.00►
◄modeproject►
◄keep_sizetrue►

Here's the thing nobody tells you when you move to a modern video or distilled model: the negative prompt box is a decoration. At CFG 1 there is no unconditional pass, so there's nothing for the negative to steer against. You can type "blurry, warped hands, extra fingers" in there forever and it does literally nothing. Most people find out by testing it and feeling like an idiot.

FunPack Negative Erase tries something else. Instead of hoping the sampler will use the negative, it edits the positive conditioning before the sampler ever runs: it works out the direction the negative prompt occupies in the encoder's embedding space and removes that direction from the positive prompt's words.

What it's actually computing

The negative prompt is pooled into a single unit vector - its "direction". If the encoder tags its rows (the source looks for H3's minimax_token_tags to find the text rows and ignore the video ones), only the text rows are pooled. Then two modes:

project (the default) subtracts the component of each positive text row that lies along that unit vector: h − a·(h·n)n. A word at right angles to the negative is untouched; at strength 1, the part of the word pointing at the negative is gone entirely. This is the mathematically clean version and the one to try first.

subtract pulls each row away from the negative by a share of its own magnitude. Blunter, and it moves everything, not just the aligned part.

keep_size renormalises each row back to its original length afterwards - direction changes, magnitude doesn't - with a cap at 2x, because a word that was the unwanted concept would otherwise come back as amplified rounding error.

The author is refreshingly honest about the ceiling in the module docstring: whether a plain sentence has a clean linear presence in the encoder is unproven, and nouns ("a hat") should work better than adjectives ("ugly").

Inputs, outputs, and the one you'll forget

positive, negative, strength, mode, keep_size. That's it - no text fields, no clip.

strength defaults to 0, which means off, and the positive passes through untouched. This is the single most common "it doesn't work" cause: the node shipped inert on purpose so it can sit in a workflow without changing it, and you have to turn it up. Values around 0.5 to 1.0 are the interesting range; 1 removes the whole aligned component and past that you're in overshoot territory.

Outputs are positive and status. Wire positive to the sampler's positive input and drop the negative prompt connection entirely - the point of CFG 1 is that you don't run the unconditional branch, and this node is what buys back some of what you lose by not running it. status tells you what happened, and it does not lie: "off", "there is no negative conditioning to take out", or a note that the negative was empty and had no usable direction.

Install

cd ComfyUI/custom_nodes
git clone https://github.com/digital-garbage/ComfyUI-FunPack
cd ComfyUI-FunPack && pip install -r requirements.txt

Restart. ComfyUI Manager works by searching ComfyUI-FunPack, with the README's caveat that Registry publishes lag and Settings ▸ About FunPack in the /funpack/ UI is how you check your version. Dependencies are comfy-kitchen>=0.2.32 and gguf; no models to download, since this works on conditioning tensors you already have.

Where the idea comes from, and where it stops

The "negative prompting without CFG" problem is well-known enough to have a proper solution: NAG, Normalized Attention Guidance, which intervenes inside the attention layers so you get negative guidance without a second full pass. That's the reason people on CFG-1 setups can use a negative prompt at all, and the discussion around it was clear about the mechanism - the same positive/negative comparison, applied at the end of each attention layer instead of once per step.

Negative Erase is the cheap cousin: pure torch, no attention hooks, one projection in conditioning space. Less powerful, far less invasive. What it will do is nudge a clip away from a look you keep getting - a colour cast, a rendering style you can't shake. It will not give you SDXL-grade negative control; if you need that on a distilled model, NAG is the tool.

A word of warning from the source, which I appreciate: the node never hands on a non-finite conditioning. If the arithmetic produces NaNs it returns your original positive unchanged. So the failure mode is "no effect", not "garbage output" - which is the right way round, and also why you should read status rather than assume a strength of 1.5 did something dramatic.

Common issues

Nothing changes. strength is 0. That's the default.

Status says the negative is empty. Your negative conditioning carries no usable direction - an empty prompt, or one the pooled vector can't be built from. Give it a real sentence.

It changes everything, not just the thing you wanted gone. You're using subtract, which drags every row. Switch to project, and lower strength.

It only works on H3-ish encoders. The text-row detection is H3-specific; elsewhere it falls back to using every row, which pulls in conditioning you didn't mean to touch. The pack's tested model is MiniMax H3, SD 1.5 is experimental, LTX 2.x is untested. Treat results on anything else as an experiment.

CategoryFunPack/Conditioning

Inputs (5)

NameTypeDefaultDescription
positiveCONDITIONING—
negativeCONDITIONING—
strengthFLOAT0.000–20 = off. How much of the negative prompt is taken out of the positive prompt's words (1 = all of it).
modeCOMBOprojectproject: removes the part of each word that lies along the negative's direction. subtract: pulls every word away from the negative by a share of its own size.
keep_sizeBOOLEANtruePut each word back on its original size, changing its direction only.

Outputs (2)

NameTypeDescription
positiveCONDITIONING—
statusSTRING—