Image Palette Extractor
Turn any image into an 8-color palette — without guessing
- image
- custom_json
ComfyUI has a billion nodes for making images and almost none for reading the colors that are already in them. Image Palette Extractor - from the same TJ_ComfyUI_ColorUtility pack as RGB Color Picker - fixes that: feed it an image and it hands you the eight colors that best represent it, as a JSON string you can feed downstream. If you've ever wanted a batch of generations to match the mood of a reference photo, or needed a palette pulled from brand art without opening a separate color tool, this is the node.
How it works - the interesting part
The code does real color science instead of the naive "sample a few pixels" trick. First it converts the IMAGE tensor to numpy RGB in 0–1 space, tolerating both channel layouts ComfyUI uses. If the image has more pixels than sample_max_pixels, it randomly samples down to that cap, seeded so results are repeatable. Then it converts sRGB to CIELAB - the perceptual color space where Euclidean distance roughly matches how different two colors look to a human - and runs k-means clustering with k=8, using k-means++ initialization for 15 iterations.
The merge_delta knob is the one you'll actually tune. After clustering, any two cluster centers closer than that threshold (in Lab distance) get merged, weighted by cluster size, so near-duplicate shades collapse into one representative. Lower it and the palette keeps subtle distinctions; raise it and similar colors blur together. It then guarantees exactly eight clusters, converts the centers back to sRGB, and emits a single STRING in this shape:
{"colors": ["#RRGGBB", "#RRGGBB", "#RRGGBB", "#RRGGBB", "#RRGGBB", "#RRGGBB", "#RRGGBB", "#RRGGBB"]}
The inputs that matter
image- any IMAGE. The actual required input.sample_max_pixels(default 100,000) - cap on pixels fed to clustering. With k=8 you rarely need more; a 4K frame downsampled to 100k pixels clusters to basically the same palette far faster. Raise it only if you genuinely need to catch a tiny accent color.merge_delta(default 6.0) - the "how close is too close" threshold in Lab space. The main quality dial.sort-frequency(default),hue, orluminance- controls the order of the eight output slots.seed(default 42) - makes the random sampling and cluster init deterministic.
Where it wires in
The output is a single JSON string, which is the one real beginner trap: this node emits text, not swatches. The natural destination is the same pack's Color Palette node - set its preset to custom and paste in the JSON, and it parses the string into eight separate #RRGGBB outputs you can route anywhere. Show Text (comfyui-custom-scripts) works too if you just want to read the palette. There's no built-in "render the palette as an image" step, so if you want a visual strip you'll pipe the hex strings into a solid-color/fill node yourself.
How to install it
Same as its packmate - no requirements.txt, no models, nothing to download beyond the code itself. ComfyUI Manager, search "TJ_ComfyUI_ColorUtility", or:
cd <ComfyUI>/custom_nodes
git clone https://github.com/TJ16th/TJ_ComfyUI_ColorUtility.git
restart, done. The only real dependency is numpy, which ComfyUI already ships.
Gotchas
- Output is a string, not an image - see above. The JSON has to be parsed by whatever consumes it; ColorPalette's
custompreset handles it, most generic nodes won't. - New pack, no community track record. The defaults are sensible (100k samples, delta 6, k=8), so start there and only touch
merge_deltaandsortuntil you have a reason. - If the same image gives you a different palette run-to-run, check your seed - it drives both the sampling and the k-means initialization, so at a fixed seed the output should be stable.
Inputs (5)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | — | |
| sample_max_pixels | INT | 1000001000–2000000 | — |
| merge_delta | FLOAT | 6.00–50 | — |
| seed | INT | 420–2147483647 | — |
| sort | COMBO | frequency | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| custom_json | STRING | — |