Skip to content
PixelKit

Guide

Building a colour palette from a photo

From pixels to a usable palette: what extraction algorithms actually do, how to pick roles for each colour, and how to keep text readable.

Last reviewed 2026-11-10

A good photograph already contains a colour scheme that works: the light, the materials and the setting agree with one another. Pulling a palette from a photo is a fast way to brand a product page, a social post or a presentation so that it feels connected to the imagery. But an extracted palette is raw material, not a finished system. Here is what the algorithms do, and how to turn their output into something you can design with.

A photo has thousands of colours

A 12-megapixel photo can contain hundreds of thousands of distinct RGB values. Most differ from their neighbours by a few units, invisible on their own. The job of palette extraction — technically called colour quantisation — is to group those values into a handful of representative colours, and to report how much of the image each one covers.

The first step is usually to shrink the image. Dominant colours survive downsampling, and working with a few tens of thousands of pixels instead of millions makes the process take milliseconds rather than seconds.

Median cut: divide the colour space

Imagine every pixel as a point inside a cube, with red, green and blue as its three axes. Median cut, described by Paul Heckbert in 1982, starts with one box around all the points. It finds the axis along which the box is longest, sorts the points along that axis and splits the box at the median, so each half holds the same number of pixels. It then repeats on whichever box is most spread out, until it has as many boxes as colours requested. The average colour in each box becomes a palette entry.

Median cut is deterministic: the same image always gives the same result. It also naturally gives more entries to colour regions that cover more of the image, which matches what we perceive as dominant.

K-means: pull colours toward their clusters

K-means takes a different route. It starts with k guesses, assigns each pixel to the nearest guess, moves each guess to the average of its pixels, and repeats until nothing moves. The result is a set of colours that each sit at the centre of a tight cluster.

Its weakness is the starting point. Random initial guesses can give different palettes on each run, or leave two guesses competing for the same cluster. A common remedy, and the one PixelKit uses, is to seed k-means with the output of median cut: the boxes provide well-spread starting colours, and k-means then refines them.

Reading the result critically

Extracted palettes have predictable quirks:

  • Averaging mutes colour. A swatch is the mean of many slightly different pixels, so it is often less saturated than the most vivid part of the region. Nudge saturation up if the swatch looks dull next to the photo.
  • Small accents disappear. A red tag on a grey sweater might cover 1% of the frame and be merged into a neighbour. Raise the colour count, or crop tightly around the accent and extract again.
  • Shadows and highlights count as colours. Expect a near-black and a near-white in most photos. They are often useful as text and background colours.
  • RGB distance is not perception. Equal numerical distances do not look equally different to the eye, particularly in blues and greens.

From swatches to roles

A usable palette assigns every colour a job. A simple, durable structure is:

  1. Background — the largest area. Usually a light or dark neutral from the photo, perhaps softened.
  2. Surface — cards and panels, a step lighter or darker than the background.
  3. Text — the darkest (or lightest) swatch, adjusted until it has strong contrast with the background.
  4. Accent — the most distinctive saturated colour, used sparingly for buttons, links and highlights.
  5. Support — one or two secondary colours for illustrations, tags or charts.

Using a colour's share of the image as a guide works well: dominant colours tend to make calm backgrounds, while colours with small shares but high saturation make good accents. This mirrors the photograph itself, where vivid colours are usually the point of interest rather than the setting.

Keep text readable

The most common failure of photo-derived palettes is low-contrast text: a warm mid-tone heading on a warm mid-tone background. Check every text and background pairing against the WCAG 2.x contrast ratio, which compares the relative luminance of the two colours on a scale from 1:1 to 21:1:

  • At least 4.5:1 for normal body text (level AA).
  • At least 3:1 for large text — 24 px regular or about 18.7 px bold — and for icons and the borders of form controls.
  • At least 7:1 for body text if you aim for level AAA.

If a brand colour fails, keep its hue and adjust its lightness: darken it for text on light backgrounds, lighten it for text on dark ones. In HSL terms, change L and leave H alone, and the colour will still feel like the same family. Reserve the unmodified, vivid version for large shapes and buttons with their own contrasting label colour.

Exporting for real work

Once roles are decided, write them down as variables. CSS custom properties make this straightforward:

--color-bg, --color-surface, --color-text, --color-accent

Naming by role rather than by hue (--accent, not --orange) means you can retune the palette for a new campaign photo without rewriting every rule. Keep a JSON copy of the palette with HEX and RGB values too, so the same colours can be used in design tools, slide templates and email builders.

A worked workflow

  1. Choose a photo that represents the product or campaign, with the lighting you want the brand to feel like.
  2. Extract six to eight colours. Note which have the largest shares and which are most saturated.
  3. Assign roles: neutral background, darker text, saturated accent.
  4. Test text on background and accent-button label on accent. Adjust lightness until each pair passes AA.
  5. Export as CSS variables, rename them by role, and preview a real page before committing.

Done this way, a palette pulled from a photo is more than a mood board: it is a small, tested colour system that stays faithful to the image and works for every reader.