Extracting Colors from Images

May 28, 20267 min read

Why Extract Colors from Images?

Every photograph, illustration, and screenshot contains a hidden palette. Extracting dominant colors lets you build designs that feel visually connected to their imagery — product pages that match hero photos, dashboards themed around user avatars, or brand systems derived from a mood board.

Manual color picking with an eyedropper is slow and subjective. Algorithmic extraction gives you consistent, repeatable palettes in seconds.

How Color Extraction Works

Simple histogram method

The most basic approach counts every pixel and ranks colors by frequency. The problem: images contain millions of pixels, and raw frequency rewards large uniform areas (backgrounds, skies) over the visually significant colors you care about.

Median cut algorithm

Median cut divides the color space into boxes, then recursively splits the box with the largest color range until you have the desired number of boxes. The average color of each box becomes a palette entry.

All pixels (one box)
  → Split along the channel with the widest range
  → Two boxes
  → Split the box with the largest range again
  → Continue until N boxes
  → One representative color per box

Median cut produces balanced palettes because it prioritizes color diversity over pixel frequency. A small but visually important red accent gets represented alongside the large blue sky.

K-means clustering

K-means groups pixels into K clusters by minimizing the distance between each pixel and its cluster center. It is more accurate than median cut but computationally heavier — fine for tooling, impractical for real-time use.

Which method to use?

MethodSpeedQualityBest For
HistogramFastLow — biased by areaQuick prototypes
Median cutMediumGood — balancedMost production use
K-meansSlowBest — perceptualPrecision palette generation

Practical Extraction Workflow

Step 1: Choose the right image

Extraction works best on images with clear color subjects. Avoid:

  • Overexposed photos (washed-out palette)
  • Images with heavy filters (distorted color distribution)
  • Low-resolution sources (not enough pixel diversity)

Step 2: Extract 5–8 dominant colors

Five to eight colors give you enough variety for a full interface palette — primary, secondary, accent, background, and neutral shades. Fewer than five feels limiting; more than eight becomes hard to distinguish.

Step 3: Clean the palette

Raw extraction includes near-duplicates and visually irrelevant colors (a single black pixel from a shadow). Clean by:

  • Merging colors within a small ΔE distance (< 5)
  • Removing colors below a 2% frequency threshold
  • Replacing gray/neutral entries with intentional neutral scales

Step 4: Assign roles

Map extracted colors to UI roles:

RoleSelection criteria
PrimaryMost saturated, visually dominant
SecondarySecond most saturated, complements primary
AccentHigh contrast against primary — often warm if primary is cool
BackgroundLightest extracted color or derived light neutral
SurfaceSlightly darker than background
TextDarkest extracted color, verified for contrast

Not every extracted color needs a role. Some are scratch candidates that inform the final choices without being used directly.

Extracting Colors in Code

Browser-based with canvas

function extractColors(imageElement, colorCount = 5) {
  const canvas = document.createElement('canvas')
  const ctx = canvas.getContext('2d')
  canvas.width = 50   // Downsample for speed
  canvas.height = 50
  ctx.drawImage(imageElement, 0, 0, 50, 50)

  const imageData = ctx.getImageData(0, 0, 50, 50).data
  const colors = {}

  for (let i = 0; i < imageData.length; i += 4) {
    const r = imageData[i]
    const g = imageData[i + 1]
    const b = imageData[i + 2]
    // Quantize to reduce noise
    const key = `${Math.round(r/16)*16},${Math.round(g/16)*16},${Math.round(b/16)*16}`
    colors[key] = (colors[key] || 0) + 1
  }

  return Object.entries(colors)
    .sort((a, b) => b[1] - a[1])
    .slice(0, colorCount)
    .map(([key]) => {
      const [r, g, b] = key.split(',').map(Number)
      return { r, g, b, hex: '#' + [r, g, b].map(v => v.toString(16).padStart(2, '0')).join('') }
    })
}

This simplified histogram approach works for quick extraction. For production quality, use a library like colorthief or quantize that implements median cut.

Node.js with Color-Thief

import ColorThief from 'colorthief'

const ct = new ColorThief()
const palette = await ct.getPalette('./photo.jpg', 5)
// Returns array of [r, g, b] arrays

Use Cases

Use caseHow many colorsNotes
Product page matching hero image3–4Primary accent derived from product photo
Dynamic avatar themes2–3Extract from profile picture
Brand palette from mood board5–8Curate extracted colors, refine manually
Data visualization palettes6–10Ensure perceptual uniformity between entries
Accessibility-themed palettes4–6Verify contrast after extraction

Key Takeaways

  • Color extraction reveals the hidden palette inside any image
  • Median cut algorithm balances diversity and fidelity for most use cases
  • Extract 5–8 colors, then clean duplicates and low-frequency noise
  • Map extracted colors to UI roles rather than using them raw
  • Always verify WCAG contrast after extraction — dominant does not mean accessible
  • Browser canvas provides a quick extraction method; use dedicated libraries for quality palettes

Try It Yourself

Pick and refine colors visually with our free Color Picker. Choose from any source, convert between formats, and copy production-ready color values instantly.