Colours from a photo: a palette that passes contrast

How to pick reliable colours from a photo, why the dominant palette rarely gives readable text, and how to fix each pair with real WCAG ratios.

On this page

Building a colour scheme from a photograph is one of the oldest design shortcuts: a product shot, a landscape or a logo already has colours that belong together. The trouble starts when those colours have to carry text. This article goes from photo to working palette with real numbers: picking colours reliably, extracting the dominant ones, and checking every text and background pair against WCAG. All ratios were computed with the same contrast code the image colour picker uses.

Picking one colour reliably

Zoom into any photo and a flat-looking surface turns out to be a mosaic. A "white" wall is a spread of slightly different whites, JPEG compression adds 8 × 8 blocks with their own tint, and the edges of objects blend into their neighbours. Clicking one pixel gives you whichever of these you happened to hit.

Two habits fix it:

  • Average a small square. The colour picker can read one pixel, a 3 × 3 average (9 pixels) or a 5 × 5 average (25). Averaging cancels most of the noise and gives the colour of the surface.
  • Look at the magnifier, not the photo. It shows the 11 × 11 pixels under the cross, so you can see when you are on an edge or a highlight and move off it.

On a phone, drag your finger and lift it to pick: the magnifier sits beside the picture, so your fingertip does not cover the spot.

Extracting the dominant colours

For a whole palette, the picker uses median cut. It counts the picture's pixels in a histogram (32 levels per channel, 32,768 buckets), puts every colour in one box, and repeatedly splits the box with the largest spread at the median of its widest channel. Each final box becomes one colour, the exact average of its pixels, with the share of the picture it represents.

Two properties matter in practice:

  • It is deterministic. k-means, the other common method, starts from random centres and can return a different palette on every run. Median cut returns the same colours for the same picture every time, so a palette you shared yesterday can be reproduced.
  • It follows area, not importance. A small red logo on a big grey background may not appear at all in a 4-colour palette, because the grey shades take the boxes. Ask for more colours (10 to 16), or pick the logo colour by hand.

A worked example

Say a photo of a harbour gives this palette, largest share first: a deep navy #143c8c (water), a warm orange #f08c1e (boats), a creamy #f6efe4 (sails) and a dark green #2f5d3a (hills). Here is how the pairs score against each other (WCAG 2 contrast ratio):

TextBackgroundRatioBody text (4.5:1)Large text (3:1)
#f6efe4 cream#143c8c navy8.95:1PassPass
#143c8c navy#ffffff white10.22:1PassPass
#2f5d3a green#f6efe4 cream6.69:1PassPass
#143c8c navy#f08c1e orange4.12:1FailPass
#2f5d3a green#f08c1e orange3.08:1FailPass
#f08c1e orange#ffffff white2.48:1FailFail
#f08c1e orange#f6efe4 cream2.17:1FailFail
#143c8c navy#2f5d3a green1.34:1FailFail

The pattern is typical. The darkest and lightest colours of a photo pair well. The saturated accent, the colour that made the photo attractive, fails as text on light backgrounds, and two mid-dark colours are almost indistinguishable to someone with low vision even though they look different in hue.

Fixing the pairs that fail

Keep the hue, change the lightness. For the orange on white, the picker's contrast check suggests #b1620c: the same hue (31° in HSL) with the lightness lowered from 53% to 37%, which reaches 4.5:1. Against the cream background it suggests #a25a0b, slightly darker still, because cream is a little darker than white.

That gives a practical rule for photo palettes:

  • Use the photo's accent as a fill (buttons, highlights, illustrations), with dark text on it: black on #f08c1e is 8.48:1.
  • Use a darker shade of the accent for text and links on light backgrounds.
  • Do not put two mid-tones together for anything that has to be read.

OKLCH makes this easier to do by hand. The orange is oklch(73.2% 0.163 60.6); lowering only the L value darkens it while keeping the perceived hue and saturation, which HSL does not guarantee. The picker shows OKLCH for every colour for this reason.

Exporting it

Once the pairs work, export the palette as code: CSS custom properties (\--palette-1: #143c8c;\), a Tailwind v4 \@theme\ block or v3 config, SCSS variables, JSON with the OKLCH values and shares, or a plain list for a design tool. Rename the prefix to your brand before copying.

Common mistakes

  • Picking a single pixel from a JPEG. Average a 3 × 3 or 5 × 5 square.
  • Trusting the eye for contrast. Saturated orange on white looks bold but measures 2.48:1.
  • Using the palette in the order it comes. The largest share is often a background colour; the accent may be fourth.
  • Checking only against white. Test against the actual background you will use, cream and light grey change the numbers.
  • Forgetting icons and borders. WCAG asks 3:1 for interface parts too; the green on orange pair barely passes that.

Next steps

Open your photo in the image colour picker, extract 5 to 8 colours, and run each text and background pair through the contrast check. To build tints, shades and harmonies from the colour you keep, continue in the colour tools.

Tools mentioned in this article

Back to the blog