Nodes/Concept Steer/Diff Features
ComfyUI Node

Diff Features

Stop guessing which features matter — let the diff between two prompts tell you

By Nynxz·Created 6 months ago·Updated 6 months ago· 4
Diff Features
  • conditioning_a
  • conditioning_b
  • VISUALIZATION
  • DIFF_REPORT
  • A_FEATURES
  • B_FEATURES
sae_path
top_k15
min_diff0.010
sae_expansion8
transcoder_repo
pool_modemean
save_lensfalse
lens_namediff_discovery
dict_path
protect_existingtrue

The pack's own docs call this its most powerful research tool, and they're not wrong. Instead of fishing through Feature Map hoping to spot which features matter, you hand Diff Features two conditionings - say "cinematic photo" and "snapshot photo" - and it runs both through your SAE, finds the features that fire differently, and hands you a ready-to-paste list for Feature Gate or Feature Probe. It's a hypothesis generator: it tells you what's actually different between two concepts in your encoder's feature space, before you spend any GPU on probing.

How it works

Both conditionings get encoded through the SAE to sparse feature activations. The node then diffs them - per feature, abs(activation_a - activation_b) - keeps the biggest differences above a threshold, and reports them per side. Features that light up in A but not B are "what A has that B doesn't," and vice versa. pool_mode controls whether activations are averaged across tokens (default mean) or max-pooled.

The clever bit is save_lens. Enable it and the differential direction gets saved as a .pt lens in lenses/ - which means the difference between two concepts becomes a steerable lens. That's a genuinely neat workflow: "cinematic minus snapshot" as a reusable direction you can apply, attenuate, or invert with the regular Concept Steer node. The brief even names the pattern: instead of guessing features, let the diff tell you what makes "oil painting" different from "watercolor."

The inputs that matter

  • conditioning_a / conditioning_b - the two conditionings to compare (e.g. "cinematic photo" vs "snapshot photo"). Encode each with a CLIP Text Encode.
  • sae_path - the SAE both sides decompose through. Must be the same one used in Feature Map/Gate.
  • top_k - top differential features per side (15 default, 3–50).
  • min_diff - minimum absolute difference to include a feature. Raise to filter noise; lower to see subtle differences. This is the dial you'll actually tune.
  • sae_expansion / transcoder_repo - match your SAE, or point at a pretrained transcoder repo.
  • pool_mode - mean recommended per the tooltip.
  • save_lens / lens_name / protect_existing - save the discovered difference as a lens (auto-renames _v2 etc. so it won't clobber).

Outputs: VISUALIZATION (IMAGE), DIFF_REPORT (STRING), plus A_FEATURES and B_FEATURES - the paste-ready feature lists per side.

Installing

Standard pack install - ComfyUI Manager search "Concept Steer", or git clone https://github.com/Nynxz/ComfyUI-ConceptSteer into custom_nodes/ and restart. Needs a trained SAE (or transcoder repo) and transformers/safetensors. Note the conditioning inputs come from your text encoder at the same layer the SAE targets, so keep layer/expansion consistent.

Common gotchas

  • min_diff is the noise filter and it's easy to forget. At the 0.01 default you'll see a long tail of near-zero diffs. If the report is noisy, raise it.
  • The diff is only as meaningful as the SAE. A weak SAE (synthetic data, bad L1) produces diffs between noise, not concepts. Fineweb-trained or pretrained transcoders give far cleaner answers.
  • Same SAE everywhere. Decompose both conditionings and your downstream gates with one consistent SAE or the indices drift into meaninglessness.
  • Prompt phrasing biases the diff. "Cinematic photo" vs "snapshot photo" differs mostly in style because the content is held constant - that's the intended setup. If your two prompts also differ in subject, the diff mixes style and content. Keep content fixed when you want a style-only answer.
CategoryConcept Steer/Research

Inputs (12)

NameTypeDefaultDescription
conditioning_aCONDITIONINGFirst conditioning (e.g. 'cinematic photo')
conditioning_bCONDITIONINGSecond conditioning (e.g. 'snapshot photo')
sae_pathSTRINGAbsolute path to SAE weights (.pt file). Must be the same SAE used in Feature Map/Gate.
top_kINT153–50Number of top differential features to show per side
min_diffFLOAT0.0100–1Minimum absolute difference to include a feature. Raise this to filter noise. Lower to see subtle differences.
sae_expansionINT82–128SAE expansion factor (auto-detected for transcoders). 8x for trained SAEs, 64x for pretrained transcoders.
transcoder_repoSTRINGHuggingFace repo for pretrained transcoders (e.g. 'mwhanna/qwen3-4b-transcoders'). Auto-downloads on first use. Leave empty to use sae_path instead.
pool_modeCOMBOmean'mean' = average across tokens (recommended). 'max' = max activation per feature across tokens.
save_lensBOOLEANfalseSave the differential direction as a .pt lens file in the lenses/ directory. Use with Concept Steer to apply the discovered concept difference.
lens_nameSTRINGdiff_discoveryName for the saved lens file (only used when save_lens is enabled).
dict_pathSTRINGPath to feature dictionary JSON (from Feature Dictionary node). When provided, differential features are labeled.
protect_existingBOOLEANtrueIf the lens file already exists (save_lens=True), save as _v2, _v3, … instead of overwriting.

Outputs (4)

NameTypeDescription
VISUALIZATIONIMAGE
DIFF_REPORTSTRING
A_FEATURESSTRING
B_FEATURESSTRING