Building a Usable Colour Palette from a Source Image
Automated tools extract colours but cannot verify contrast. Learn the manual refinement process that makes an image-derived palette accessible and production-ready.
Building a Usable Colour Palette from a Source Image
You upload a photo with a colour combination that works. The five hex codes your tool spits out produce text your audience cannot read. Extracting a palette from an image is easy. The hard part: no automated extraction tool checks WCAG 2.2 contrast ratios. That step is manual and non-negotiable. Run the source image through Adobe Color or Coolors, get five dominant swatches, then find that the mid-tone you liked fails a 4.5:1 ratio on white. Adjust lightness, not hue or saturation, until every pair passes. Here is how.
Colour Palette Extraction Tool: What It Actually Does
A colour extraction tool, Adobe Color, Coolors, Canva, uses colour quantisation to reduce your image to a handful of representative swatches. Two algorithms dominate. Median cut, published by Paul Heckbert in 1982, partitions colour space into buckets of roughly equal pixel count and picks the centroid of each bucket. K-means clustering, published by Stuart Lloyd the same year, iteratively refines cluster centres to minimise within-cluster variance. Both are implemented in OpenCV's cv2.kmeans() and Pillow's Image.quantize().
These tools sample dominant pixels. They do not evaluate whether those pixels work as a foreground/background pair. Adobe Color's extraction rules panel gives you five mood presets, Colorful, Bright, Muted, Deep, Dark, but none adjusts the output for relative luminance. Coolors generates a five-swatch set from an uploaded image using dominant colour detection. Canva limits you to four main swatches. In every case, the output is a set of sRGB hex codes that look good in a grid and fail on a live text overlay.
The failure mode is predictable. Posterisation or colour quantisation produces muddy mid-tones that fall below a 3:1 or 4.5:1 ratio. A swatch sitting at 50% lightness in HSL will not pass on white, regardless of how harmonious it looks in the tool's preview.
Manual Palette Refinement: The Only Step That Fixes Contrast
Manual refinement means taking the extracted hex codes and adjusting them until every pair in your palette meets the WCAG 2.2 minimum ratio for its intended use. The ratios are: 4.5:1 for AA normal text, 3:1 for AA large text (18pt or 14pt bold), 7:1 for AAA normal text, and 4.5:1 for AAA large text. UI components and graphical objects require 3:1.
The verification method is the relative luminance formula for sRGB: L = 0.2126 × R + 0.7152 × G + 0.0722 × B, where R, G, B are the linearised channel values, not the 0-255 gamma-compressed values. Linearise by dividing each channel by 255, then applying the sRGB transfer function (gamma ≈ 2.2). Then plug the two luminances into the ratio formula: (L1 + 0.05) / (L2 + 0.05), where L1 is the lighter luminance. A passing ratio on paper may fail on a dimmed mobile screen in sunlight. Test on the actual target device.
To refine, work in HSL. Keep hue and saturation constant; adjust only lightness. If a colour fails on white, reduce its lightness below 30%. If it fails on black, raise its lightness above 70%. This preserves the colour harmony you chose while forcing the pair into a passing ratio. The alternative, changing hue to find a naturally higher-ratio pair, shifts the palette away from the image that inspired it.
From Image to Brand Swatches: A Worked Example
You have a source image with a dominant teal at 45% lightness. On white, the ratio is 3.2:1, below the 4.5:1 AA minimum for normal text. To reach 4.5:1, drop lightness to 25%. That produces a darker teal that reads as the same hue. On black, the same original teal at 45% lightness yields a ratio of 2.8:1, below the 3:1 AA minimum for large text. Raise lightness to 75%. That produces a pale teal that still matches the brand's blue-green direction. The result is a two-value colour: a dark variant for text on light backgrounds, a light variant for text on dark backgrounds, both derived from the same extracted swatch.
This is how you go from image to brand swatches without failing accessibility. The extracted colour becomes a colour family, not a fixed hex code.
WCAG 2.2 Palette Verification: The Workflow
Compute Ratios for Every Pair
Run a three-step process on every pair you intend to use. First, extract the sRGB 8-bit values from your hex code. Second, compute relative luminance for each colour using the formula above. Third, calculate the ratio. Repeat for every foreground/background combination in your palette.
Do not trust a contrast-checker plugin's first result. The claimed-vs-real problem: a plugin may sample a rendered pixel whose antialiasing blended foreground and background, inflating the ratio by up to 1.5:1. The cause is subpixel rendering and font smoothing altering edge pixels. Verify by sampling a solid area, not the edge of a glyph, at actual rendering size.
The tool's extraction rule (Adobe Color's Color Themes panel extracts five swatches) gives you a starting set. The manual step is the one that matters. When producing a palette for a brand system, test every pair against the three WCAG levels: AA normal text (4.5:1), AA large text (3:1), and AAA normal text (7:1). The 3:1 ratio also applies to UI components and graphical objects, your button backgrounds and their text labels.
Watch for Gamut Clipping
Gamut clipping is a related risk. An extracted colour that looks strong in sRGB may desaturate when converted to CMYK for print. The colour difference formulas, CIE76 ΔE*ab (Euclidean distance in CIELAB), CIE94 ΔE*94 (weighted), and CIEDE2000 (most accurate), quantify this shift. A just noticeable difference (JND) threshold is ΔE*ab ≈ 1.0; a perceptible difference for untrained observers is ΔE*ab ≈ 2.3. If your palette passes on screen but the printed material fails because the swatches shifted, you have a gamut problem, not a ratio problem.
The Honest Caveat: No Tool Replaces Visual Verification at Actual Size
You can automate the extraction, compute relative luminance with a spreadsheet, and generate a palette that passes every WCAG 2.2 ratio in isolation. Then you put it on a live page and it fails. The reason: the contrast-checker plugin sampled an antialiased edge pixel, the font rendering engine softened the edge, or the background texture reduced the effective ratio. The only reliable test is visual verification at actual rendering size on the target device. That step cannot be automated, and it is the one most often skipped.
Common Questions
How many colours does a typical extraction tool give me?
Adobe Color extracts five colours from an image. Coolors generates five. Canva limits you to four. The maximum palette size for an indexed PNG or GIF is 256 colours, but for a usable brand palette refine no more than five.
Can I use a colour harmony scheme to fix contrast?
No. Colour harmony, complementary, split-complementary, triadic, tetradic, analogous, monochromatic, describes hue relationships on a colour wheel, not luminance differences. A triadic scheme spaced 120° apart can still fail a 4.5:1 ratio if all three colours sit at the same lightness. Adjust lightness per colour, not hue.
What if my extracted colour is a pastel that cannot reach 4.5:1 on white?
You cannot raise its lightness further; it is already light. You darken it. Drop lightness below 30% in HSL. The hue and saturation stay the same. The colour becomes a dark variant of itself.
Does the relative luminance formula change for Display P3 or Adobe RGB?
The formula L = 0.2126 × R + 0.7152 × G + 0.0722 × B is defined for sRGB primaries. Display P3 (red x=0.680, y=0.320; green x=0.265, y=0.690; blue x=0.150, y=0.060; D65 white) and Adobe RGB (red x=0.6400, y=0.3300; green x=0.2100, y=0.7100; blue x=0.1500, y=0.0600; D65 white) have different primaries, so the coefficients differ. For web delivery, use sRGB. For print, use the ICC profile for your output condition.
What is the most common failure when extracting a palette from an image?
Trusting the tool's output without manual refinement. The tool samples dominant pixels but does not check ratios. A muddy mid-tone that looks fine in a swatch grid fails on white text. Adjust lightness until the pair passes, then re-verify.
How do I test contrast for UI components like button backgrounds?
WCAG 2.2 requires a 3:1 ratio for UI components and graphical objects. Measure the luminance of the button background and the text label on it. If the text is white on a coloured button, test that pair. If the button has a border, test the border against the background it sits on.
What should I do if the printer rejects my palette because of gamut clipping?
Soft-proof the palette using the printer's ICC profile before sending the file. The rendering intent matters: perceptual for photographic images, relative colorimetric for vector graphics and spot colours. If the printer expects Fogra 39 and your file is in uncalibrated sRGB, expect a 10-15% darkening toward magenta.