Elofyn / Tools / Color Palette / About

About the Color Palette Extractor

A short tour of what this tool does, why “dominant color” is a harder problem than it sounds, and how the algorithm under the hood actually works. Back to the tool ↗

What the tool does

Drop an image into your browser. In about a second you get a 3- to 12-swatch palette — each swatch labeled with its HEX, RGB, HSL, and the approximate share of the image it represents. You can click any value to copy it, export the whole palette as a CSS custom-property block, a Tailwind theme.colors snippet, or a JSON file, and download a 1200×200 PNG strip for pasting into design docs. Everything runs inside your browser: the image bytes are decoded into a <canvas> and the clustering happens in the same JavaScript context. Nothing is uploaded. Nothing is logged. This is a property of the tool, not a promise.

The underlying problem: “dominant color” is fuzzy

Ask three people for the “dominant color” of a sunset photo and you will get three honest, different answers. The statistical modalcolor — the single most-counted RGB triple — is usually some noisy sliver of the sky. The mean color of the image is almost always a muddy, desaturated brown, because averaging in sRGB collapses saturated values toward grey. The color a designer would actually point at is the perceptually dominant color: the one that takes up a big region of the frame and that the eye fixes on. None of those three definitions is wrong; they are different questions. Good palette extractors approximate the third one by grouping similar pixels together and reporting the centers of the biggest groups, rather than trusting raw counts or averages.

A short history of color quantization

The problem of reducing a true-color image to a small palette is older than the web. In the early 1980s, frame buffers held 8 bits per pixel, which meant a displayable image could carry at most 256 distinct colors. Paul Heckbert’s 1982 paper Color Image Quantization for Frame Buffer Display introduced the Median Cut algorithm, which recursively splits the RGB color cube along its longest axis until each leaf contains roughly the same number of pixels. The centroid of each leaf becomes a palette color. Median Cut is still the conceptual backbone of colorthief and most modern in-browser palette tools, including this one. See citation [1] for the original paper.

A few years later, Gervautz and Purgathofer (1988) proposed an Octree quantization scheme that stores colors in an adaptive 8-way tree, trading memory for finer granularity in dense regions of color space. Octree is still used in encoders that need deterministic palettes (e.g. animated GIF encoders) but is fussier to tune for arbitrary photographs. By the mid-2010s, plain k-meansclustering in color space had become the default for palette extraction: it is simple, embarrassingly parallel, and gives excellent results on natural images at the cost of a few iterations. Android’s Palette API, which popularized in-app dynamic theming, layers a modified-Median-Cut step on top with semantic role assignment (vibrant / muted / dark / light).

Why color space matters

The trap in all of these algorithms is the color space the clustering runs in. sRGB — the space your image arrives in — is non-linear and perceptually uneven: a Euclidean distance of 30 between two greens feels very different from a distance of 30 between a yellow and an orange. CIE’s 2018 colorimetry standard formalises CIELAB and the ΔE*ab metric, which is designed so that equal numeric distances correspond to roughly equal perceived differences. See citation [2] for the standard.

ΔE*ab = √( (L₁ − L₂)² + (a₁ − a₂)² + (b₁ − b₂)² )

Higher-quality palette tools cluster in LAB (or its sibling OKLab/OKLCH) and only convert to HEX at the very end. The trade-off is cost: LAB conversion is a per-pixel transform with a cube root in it, which is noticeable when you are clustering a multi-megapixel photo on a phone. This tool clusters in linear RGB and reports per-swatch share by re-walking the downsampled pixel buffer and assigning each opaque pixel to its nearest palette color. That is fast and good enough for “what colors are in this image,” which is the question the tool is answering. If you need strict perceptual fidelity for a brand audit, a LAB-aware tool is a better fit.

What this extractor does specifically

On drop, we read the file into an ImageBitmap with EXIF orientation applied (so a phone photo is not analysed sideways), then downsample it to either 400 px or 800 px on the longest edge depending on the Sampling control. The downsampled canvas is handed to colorthief, which runs a modified Median Cut and returns the palette centroids. Pixels with an alpha channel below 16 are discarded before clustering, so a logo on a transparent background does not cluster a phantom “dominant black.” The share percentage is computed against the count of opaque pixels, not the canvas area, so transparency does not skew it. If you change the swatch count or sampling quality mid-extraction, the in-flight run is aborted and a new one is started; the status indicator always reflects the most recent run.

Use cases

Designers pull brand palettes off product shots before a Figma kickoff. Frontend engineers grab a quick set of CSS variables off a hero photo while prototyping. Data scientists scan a thumbnail grid of training images to look for unexpected color bias (“why is everything in this batch greenish?”). Accessibility folks pull a palette off a marketing render to sanity-check contrast against the brand’s primary text color. Anywhere “what colors are in this picture, in machine-readable form, in under thirty seconds” is the actual question, the tool fits.

Anti-patterns and limits

Heavy JPEG compression can hallucinate near-black or near-white “colors” in flat areas that were not really there in the source — the quantizer sees the noise as a cluster. Treating the input as sRGB is an assumption: images with embedded ICC profiles for wider gamuts (Display P3, Adobe RGB) are clipped into sRGB by the browser’s decoder before they reach the canvas, so the palette is faithful to what you see on screen, not necessarily to the original capture. Palettes pulled from screenshots of UIs are usually dominated by chrome (toolbars, backgrounds) rather than content; crop the screenshot to the region you actually care about before dropping it in.

How to choose N

Pick 3 when the question is “what is this brand?” — you want the two or three signature colors and nothing else. Pick the default 6 for product-shot palettes and most design work: it is enough range to cover a primary, an accent, a couple of neutrals, and highlight/shadow. Pick 12 for dataset audits and texture studies where you actually want to see the long tail of colors present, including the dim shadows and faint highlights.

References

  1. Heckbert, P. S. (1982). Color image quantization for frame buffer display. ACM SIGGRAPH Computer Graphics, 16(3), 297–307. The original Median Cut paper and the conceptual ancestor of every modern palette extractor in this lineage.
  2. CIE (2018). CIE 015:2018 Colorimetry, 4th Edition. International Commission on Illumination. The standard that defines the CIELAB color space and the ΔE*ab perceptual distance metric used by perceptually-aware palette tools.