PalettePeek

Extracting a color palette from a photo

6 min readUpdated May 2026
A wooden artist's palette with dabs of red, white, yellow and brown oil paint and a brush mixing a dark colour

Asking software for “the colors in this photo” is asking it to summarise a few million data points with six. There is no single correct answer, which is why two extractors give you two palettes from the same file. Knowing what each stage of the pipeline does turns that from a frustration into a set of controls you can use.

Downsampling comes first, and it matters

Almost every extractor starts by redrawing the image much smaller — PalettePeek uses 256 pixels on the longest side. That is not only for speed. Scaling averages neighbouring pixels, which quietly removes JPEG ringing, sensor noise and the single stray highlight that would otherwise become one of your six colors.

The averaging has a cost: thin saturated details disappear. A red neon sign two pixels wide in a 4000-pixel photo simply will not survive to the 256-pixel copy, and no algorithm downstream can recover it. If a small element is the whole point of the image, crop to it before extracting.

After scaling, the pixels are sampled again — PalettePeek caps the working set at 40 000 — and fully transparent pixels are dropped. Dropping transparency matters for logos and PNG exports, where an unhandled alpha channel otherwise contributes a large block of black or white that is not in the artwork at all.

Median cut: dividing the color cube

Median cut, described by Paul Heckbert in 1980, treats the pixels as points in a three-dimensional RGB box. It finds the channel in which the box is widest, sorts the pixels along that channel, and splits at the median so both halves hold roughly the same number of pixels. Repeat on whichever box still contributes most, and after k − 1 splits you have k groups; the average of each group is a palette color.

The method is fast, deterministic and never fails to return k colors, which is why it has survived for four decades. Splitting at the median rather than the midpoint is what makes it robust: a box that is mostly sky with a few bright pixels gets cut where the pixels are, not where the numbers are.

Its weakness is the averaging at the end. The mean of a group is not necessarily a color that appears anywhere in the photograph — average a group of saturated reds and oranges and you get a muddy brick that was never there. Large flat regions also dominate, because the algorithm counts pixels rather than interest.

K-means: letting the centroids settle

K-means, in its classic Lloyd formulation, starts from k candidate colors, assigns every pixel to the nearest one, moves each candidate to the average of the pixels it collected, and repeats until the candidates stop moving. Twelve iterations is usually plenty; PalettePeek also stops early when total movement falls below one unit of 0–255.

Seeding decides the outcome. Random seeds land in local minima and give a different palette on every run, which is why k-means++ or a median-cut seed is standard practice. PalettePeek seeds from its median-cut result, so the k-means pass refines a sensible starting palette instead of gambling on one.

In practice the k-means pass pulls centroids toward colors that really occur in the image and separates clusters that median cut merged. It costs more time, and on images with one overwhelming background it sometimes spends two of your twelve slots on near-identical variations of that background. Try both; the difference is one click.

Weighting: dominant is not the same as interesting

By area, most photographs are sky, skin, asphalt and wall. A purely dominance-based palette therefore tends to be six beiges, which is accurate and useless. Weighting fixes it: instead of counting each pixel once, count it according to how much you care about it.

PalettePeek's “prefer vivid” mode weights each pixel by its saturation raised to the power 0.75, then reduces the weight of pixels that are nearly black or nearly white. A saturated shop sign covering two per cent of the frame can then outrank a flat gray wall covering forty. The shares shown under each swatch are shares of that weighted mass, not of raw pixel count — the number answers “how much did this color matter to the result”.

Leave weighting off when you want an honest description of the image — for a background wash, a blurred placeholder, or a duotone. Turn it on when you are looking for accents to build an interface from. The two answers are both correct; they answer different questions.

From swatches to a palette you can build with

An extracted palette is raw material, not a design system. A workable interface palette is usually one near-white, one near-black, two or three neutrals in between, and one or two accents — which means most of the twelve swatches you extracted are reference, not tokens.

Check the spacing of your accents in OKLCH before committing. Two colors thirty degrees apart in hue will read as “the same color, slightly off” at small sizes; ninety degrees or more reads as a deliberate pair. If two accents are close, keep the more saturated one and drop the other rather than using both.

Photographs also skew warm or cool as a whole. A palette lifted from a golden-hour shot will have every neutral tinted toward orange, which looks lovely as a mood board and slightly ill as an application background. Neutralising the greys — dropping chroma to near zero while keeping the hue — is usually the right move, and keeps one warm neutral as a surface color.

Test it on an interface, not on a strip

Colors behave differently once they have a job. A swatch strip flatters everything, because every color gets the same amount of space and no color has to carry text. The moment one of them becomes a background and another becomes body copy, half of the combinations stop working.

Run the pairs through a contrast matrix before you build anything. You are looking for at least one pair above 4.5:1 for body text, one above 3:1 for large headings, and an accent that survives as a filled button with a legible label. If no pair clears 4.5:1, the palette needs a darker or lighter member, and the image will not provide it — derive one.

Finally, check the dark variant early. Palettes taken from bright photographs often have no member dark enough to serve as a page background, and the fix — building a 50–950 ramp from one of the accents — changes which colors you want in the first place.

The takeaway

Downsampling decides what survives, the algorithm decides how the survivors are grouped, and weighting decides which groups win. Extract twice — once plain for an honest description of the image, once with vivid weighting for accents — then test the pairs on a real layout before you name a single token.

Related toolPalettePeek · Palette extractorDrop an image and read its colors right now