CLIP Score Calculator
Did the image actually do what your prompt asked?
- image
- clip_score
- score_text
The number that tells you whether the image listened
You've queued ten renders and you genuinely can't tell if the model heard your prompt or just produced something adjacent. That's the exact moment you reach for CLIP Score Calculator (CLIPScoreNode) from the ComfyUI-CLIPScore pack. Wire an image in from VAE Decode, type your prompt into the node, run, and it hands you one number: the cosine similarity between CLIP's text embedding of your prompt and CLIP's image embedding of your render. Cosine similarity runs -1 to +1; for these models, good prompt alignment lands around 0.25–0.35.
That number means something because CLIP (the OpenAI encoder that SD and Flux conditioning grew out of) was trained to push matching image-text pairs together in embedding space. It's the same metric that built the ecosystem: LAION filtered its 5-billion-pair dataset on CLIP cosine similarity (pairs below ~0.28 were dropped), and the LAION-Aesthetics score is a linear probe on top of CLIP ViT/14. So this node measures with the exact ruler the training data was cut with. One caveat: it measures semantic agreement - whether the described thing is in the image - not beauty. A perfect 0.31 can still be an ugly picture.
How it actually works
Peek under the hood and the mechanism is straightforward. The node loads a CLIP model via Hugging Face transformers (CLIPModel.from_pretrained), converts your ComfyUI image to a PIL RGB array (dropping any alpha channel, so RGBA output from VAE Decode is handled for you), and embeds both the image and your prompt. Text and image embeddings are L2-normalized and the dot product becomes the score.
Two details worth knowing:
- Long prompts get chunked. The prompt is split into 200-character slices, each scored separately (the processor also truncates to CLIP's 77-token limit), and the results are averaged. So a long Flux-style natural-language prompt still gets scored end to end rather than silently cut off.
- The score is raw, not normalized to 0-1. That's intentional - it's what the README calls "research-grade." It also means scores are only comparable within the same
model_name. A 0.22 onclip-vit-base-patch16is not the same number as a 0.22 onclip-vit-large-patch14, and definitely not the same as some other pack's normalized "CLIP similarity" node. Pick one model and stick to it for comparisons.
The inputs that matter
Only three, and honestly only two you'll touch:
- image - the IMAGE you're scoring, straight from VAE Decode.
- prompt - multiline text; type the prompt you want to check against.
- model_name -
openai/clip-vit-base-patch16(default),clip-vit-base-patch32, orclip-vit-large-patch14. The default is fine for most checks; the large one is a bit more discerning and a lot more download.
It's an output node, so it doesn't feed anything downstream. It returns clip_score (FLOAT) and score_text (STRING, e.g. CLIP Score: 0.2713), prints the same line to the console, and that's it. Use it as a dead end at the end of a workflow you're A/B testing.
Install
ComfyUI Manager: search ComfyUI-CLIPScore and install. Or the manual way:
cd ComfyUI/custom_nodes
git clone https://github.com/emniko/ComfyUI-CLIPScore
cd ComfyUI-CLIPScore
pip install -r requirements.txt
Then restart ComfyUI. Requirements are torch, transformers>=4.36.0, pillow, numpy - no exotic deps, though transformers is a package you already have at a different version and pip can shuffle it, so watch for other nodes breaking after install (the classic custom-node dependency hell). First run downloads the CLIP checkpoint from Hugging Face - a few hundred MB for the base models, over a gigabyte for large. It caches after that, but you do need internet on that first run.
Where people get burned
- Zero isn't necessarily a mismatch. The node catches exceptions and returns
0.0while printingCLIP error:to the console. A 0.0 with an error line is a failure (usually the model failed to load), not a terrible score. Check the console before you trust a 0. - It re-loads the model every run. There's no session caching; each queue run reloads CLIP from the HF cache and moves it to GPU. Fine for occasional checks, sluggish for batch sweeps. Don't put it in a hot loop and expect speed.
- "Batch-first" is optimistic. The README advertises batch-first scoring, but the shipped code scores only the first image in a batch (
image[0]). Score a batch of images and you're really scoring one. Ground truth: the source, not the README.
It's also a good example of why you read a node's code before building a whole eval pipeline around it. For prompt-to-image sanity checks on a single render, this node is the one I'd reach for - small, no API key, and it reports in the currency the whole ecosystem was built on.
Inputs (3)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | — | |
| prompt | STRING | — | |
| model_name | COMBO | openai/clip-vit-base-patch16 | 3 options: openai/clip-vit-base-patch16, openai/clip-vit-base-patch32, openai/clip-vit-large-patch14 |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| clip_score | FLOAT | — |
| score_text | STRING | — |