zero-shot-image-classification Β· vision + language Β· WASM Β· ~90 MB
CLIP learned a shared space for pictures and words. Give it an image and a handful of labels
you make up on the spot β a sleeping cat, a rainy street,
cozy β and it scores how well each one matches. No training, no categories baked
in, nothing leaves your device.
| Model | Xenova/clip-vit-base-patch16 |
|---|---|
| Task / pipeline | zero-shot-image-classification |
| Architecture | ViT-B/16 vision encoder + text transformer β shared 512-d space |
| Quantization | q8 (8-bit) ONNX |
| Backend | WebAssembly (runs anywhere; no WebGPU needed) |
| Download | ~90 MB, cached after first load |
| License | MIT (OpenAI CLIP) |
| Web APIs | WebAssembly, Web Workers, FileReader β all Baseline widely available |
Pick or drop an image, type comma-separated labels, and hit Classify. The bars are a softmax over CLIP's similarity between the image and each label.

Under the hood CLIP turns your image into one 512-number vector and each label into its own 512-number vector, then measures the cosine similarity (the angle) between the image vector and every label vector. Those similarities are scaled (Γβ100, CLIP's learned temperature) into logits, and a softmax turns the logits into the percentages above. Same picture, different words β different winner. That is the whole trick.
| Label | cosine sim | logit (Γscale) | softmax |
|---|
Every other image classifier is stuck with the categories it was trained on. CLIP isn't: the label set is an input, decided at runtime, in the browser, for free. That flips a lot of problems from "collect a dataset and fine-tune a model" into "write a list of words."
weapon, graphic, safe) without shipping a training pipeline.minimalist, nostalgic, chaotic) that no fixed classifier has a class for.The whole thing is three lines with Transformers.js. The label list is just an argument:
import { pipeline } from "@huggingface/transformers";
const classify = await pipeline(
"zero-shot-image-classification",
"Xenova/clip-vit-base-patch16",
); // downloads + caches ~90 MB once
const output = await classify(imageURL, ["a cat", "a dog", "a city street"]);
// β [{ label: "a cat", score: 0.94 }, { label: "a city street", score: 0.05 }, β¦]
// score is the softmax over CLIP's imageβlabel cosine similarities
This page goes one level deeper for "See inside": it reuses the pipeline's own model,
processor, and tokenizer to read the raw logits_per_image and the image/text
embedding tensors from a single forward pass β the same call, just keeping the intermediate
numbers. Inference runs in a Web Worker so the page never janks.