Web Design

How do I extract and generate color palettes from images?

Extract dominant colors from any image with color quantization and k-means clustering — via drag-and-drop tools, the browser Canvas API, or libraries like Color Thief and Vibrant.js — then refine the palette with color theory and accessibility in mind.

By Inventive HQ Team

To extract a color palette from an image, run it through a tool or library that uses color quantization — most commonly k-means clustering or a median-cut algorithm — to reduce the image's millions of pixel colors down to the 5-10 that best represent it. Drag-and-drop tools like Coolors, Adobe Color, and Canva do this in a browser; libraries like Color Thief and Vibrant.js do it in JavaScript using the Canvas API; and Python's scikit-learn (KMeans) plus Pillow do it in a script. All of them read the pixels, group similar colors together, weight them by how much of the image they cover, and hand back the dominant colors as HEX or RGB values that you then curate into a usable scheme.

That is the summary an AI overview will give you. What it won't give you is the part that actually matters: why those algorithms produce different palettes, how to run one yourself in a dozen lines of code, and how to turn a raw extraction into a palette that is harmonious and accessible — not just a pile of HEX codes. This article walks all three.

Try it: extract a palette right now

Drop in an image and pull its dominant colors before reading further — the rest of the article explains what just happened under the hood.

Loading interactive tool...

The pipeline: image to palette in three steps

Every extraction tool, whether it's a website or a library, runs the same three-stage pipeline. You feed it an image, it samples and clusters the pixels, and it returns a compact palette of the most representative colors.

How color extraction works A three-step flow: an image is scanned pixel by pixel, its pixels are grouped into color clusters, and each cluster's average becomes one swatch in the final palette. Image → sample & cluster pixels → dominant-color palette

1. Source image

2. Cluster pixels

3. Palette #2813E8 #F59E0B #0EA5E9 #14B8A6

The key insight is in step two. Naive extraction would just count how often each exact RGB value appears — but a photo has thousands of near-identical shades of "blue," so raw counting produces a palette of imperceptibly different blues. Quantization solves this by grouping similar colors into clusters and representing each cluster with a single color, so the palette spans the image instead of clumping on its most common hue.

Which method should I use?

The right tool depends on whether you want a one-off palette, a repeatable script, or something built into an app. Here is the honest comparison.

MethodHow it worksBest forSkill neededRuns where
Manual color pickerYou eyedrop individual pixels by handA few exact brand/key colors you must controlNoneAny design app
Web tools (Coolors, Adobe Color, Canva, Colormind)Upload image → algorithmic quantization → editable paletteQuick, no-code palettes; saving to a libraryNoneBrowser (often server-side upload)
Color Thief (JS library)Canvas API + modified median cut (MMCQ)Adding extraction to a web app; client-side privacyBasic JSBrowser / Node
Vibrant.js (JS library)Quantization + swatches labeled by role (Vibrant, Muted, etc.)Themeing a UI to an image (album art, hero photos)Basic JSBrowser / Node
scikit-learn + Pillow (Python)KMeans clustering over the pixel arrayBatch analysis, data pipelines, custom kIntermediate PythonLocal / server
ImageMagick (-colors)Median-cut quantization from the CLIScripting, automation, no coding languageCLI basicsCommand line
Design software (Photoshop, Figma, Affinity)Built-in sampling + palette panelsDesigners already in the toolNoneDesktop app

Which should you pick? For a one-off palette, use a web tool — Adobe Color and Coolors are the fastest. To extract inside your own website without sending user images to a server, use Color Thief or Vibrant.js (both are client-side). For batch-processing many images or full control over the algorithm and k, use Python with scikit-learn. For shell scripts and CI, use ImageMagick.

Advertisement

Color quantization: k-means vs. median cut

Two algorithms dominate. Understanding the difference explains why tools disagree.

K-means clustering treats each pixel as a point in 3D RGB space. You choose k (how many colors you want). The algorithm places k starting centers, assigns every pixel to its nearest center, moves each center to the average of its assigned pixels, and repeats until the centers stop moving. The final centers are your palette. Because it starts from random centers, two runs can produce slightly different results — and it can be slow on large images unless you sample a subset of pixels first.

Median cut (used by Color Thief's MMCQ) takes the opposite approach. It puts every pixel in one big box in color space, then repeatedly finds the box with the widest range of color, sorts by that dimension, and splits it at the median — halving the boxes until you have the number you want. Each final box's average is a palette color. It's deterministic and fast, which is why it powers most in-browser extractors.

Neither is "correct." K-means tends to give colors weighted toward large uniform regions; median cut gives more even coverage of the color range. If a tool's palette feels off, trying one that uses the other algorithm often fixes it.

Extracting colors in JavaScript (Canvas API + Color Thief)

You don't need a library at all — the browser can read pixels directly. Draw the image to a canvas, then getImageData() returns a flat Uint8ClampedArray of [R, G, B, A, R, G, B, A, …] values you can process however you like.

// Read raw pixels with the Canvas API (the foundation every JS extractor builds on)
function getPixels(img) {
  const canvas = document.createElement("canvas");
  canvas.width = img.naturalWidth;
  canvas.height = img.naturalHeight;
  const ctx = canvas.getContext("2d");
  ctx.drawImage(img, 0, 0);
  // data is [R, G, B, A, R, G, B, A, ...] for every pixel
  return ctx.getImageData(0, 0, canvas.width, canvas.height).data;
}

// A minimal "average color" — the crudest possible extraction
function averageColor(img) {
  const data = getPixels(img);
  let r = 0, g = 0, b = 0, n = data.length / 4;
  for (let i = 0; i < data.length; i += 4) {
    r += data[i]; g += data[i + 1]; b += data[i + 2];
  }
  return `rgb(${Math.round(r / n)}, ${Math.round(g / n)}, ${Math.round(b / n)})`;
}

Averaging every pixel gives you one muddy color — useful as a placeholder, useless as a palette. For a real palette you want quantization, and that's exactly what Color Thief provides. It runs the Canvas step and MMCQ for you:

// Color Thief handles the canvas read + median-cut quantization
import ColorThief from "colorthief";

const img = document.querySelector("#source");
const thief = new ColorThief();

img.addEventListener("load", () => {
  const dominant = thief.getColor(img);        // [r, g, b] single dominant color
  const palette = thief.getPalette(img, 8);    // array of 8 [r, g, b] colors

  const toHex = ([r, g, b]) =>
    "#" + [r, g, b].map(v => v.toString(16).padStart(2, "0")).join("");

  console.log("Dominant:", toHex(dominant));
  console.log("Palette:", palette.map(toHex));
});

Two important gotchas: the image must be fully loaded before you read it (hence the load listener), and if the image comes from another domain it must be served with permissive CORS headers and loaded with crossOrigin = "anonymous" — otherwise the canvas becomes "tainted" and getImageData() throws a security error.

If you want swatches labeled by role instead of a flat list, Vibrant.js returns named results like Vibrant, Muted, DarkVibrant, and LightMuted — ideal for theming a UI to whatever image is on screen.

Doing it in Python (k-means with scikit-learn)

For batch jobs or precise control over k, Python is the cleanest path. KMeans does the clustering; Pillow loads and downsamples the image so it runs quickly.

from PIL import Image
from sklearn.cluster import KMeans
import numpy as np

def extract_palette(path, k=6):
    img = Image.open(path).convert("RGB")
    img.thumbnail((200, 200))                 # downsample for speed
    pixels = np.array(img).reshape(-1, 3)     # flatten to a list of RGB rows
    kmeans = KMeans(n_clusters=k, n_init=10).fit(pixels)
    centers = kmeans.cluster_centers_.astype(int)
    return ["#%02x%02x%02x" % tuple(c) for c in centers]

print(extract_palette("inspiration.jpg", k=6))
# -> ['#2a3d66', '#e8b04b', '#0f9d8c', ...]

Downsampling to a couple hundred pixels on the long edge barely changes the palette but makes clustering dramatically faster — a full-resolution photo has millions of pixels and k-means visits each one every iteration.

From raw extraction to a usable palette

Extraction gives you a starting point, not a finished palette. The colors that dominate an image are optimized for that image — not for legibility, harmony, or your brand. Turn the raw output into something usable:

  • Curate by role. From eight extracted colors, pick one or two dominant colors, two or three supporting colors, and one accent. A flat list of eight equal HEX codes isn't a design system.
  • Check accessibility. Extraction knows nothing about contrast. Test every text-on-background pairing against WCAG ratios (4.5:1 for normal text, 3:1 for large text and UI components) and nudge lightness where it fails. See our guide to accessible color combinations and WCAG standards.
  • Apply color theory. Ask whether the extracted colors actually form a coherent scheme — complementary, analogous, triadic — or just happen to co-occur. Our color harmonies guide covers how to adjust an extracted set into an intentional harmony.
  • Generate variations. Derive lighter and darker tints of each color for hover, disabled, and emphasis states rather than extracting them separately.
  • Store in a real format. Save the palette as HEX, RGB, and HSL — HSL makes lightness/saturation tweaks trivial. See color models (HEX, RGB, HSL, HSV) and modern CSS color formats for how to express them in code.

Generating palettes without a source image

Extraction isn't the only route. If you don't have an inspiring image, you can generate a palette from color theory instead: pick a base color and derive complementary, analogous, or triadic partners by rotating hue on the color wheel. Tools like Coolors (in generation mode), Colormind, and Khroma produce harmonious or ML-suggested schemes you can lock and regenerate. The strongest workflow combines both: extract a palette from an image for its natural harmony, then refine it with theory and accessibility checks so the result is intentional rather than accidental.

Tips for better extraction

  • Use high-quality images. Compression artifacts and low resolution muddy the clusters and produce off colors.
  • Match the image to your goal. Extract from an image whose mood matches the design you're building — a muddy source gives a muddy palette.
  • Tune the color count. Five to eight is the practical range; try a couple of values and compare.
  • Try two tools. Because algorithms differ, running the same image through a k-means tool and a median-cut tool sometimes surfaces an accent color one of them missed.
  • Keep private images local. Prefer client-side extractors (Color Thief, Vibrant.js, desktop software) when the image is confidential or copyrighted, so pixels never leave your device.

Color palette extraction turns the natural harmony already present in an image into a reusable design foundation. The tools make it a two-second operation; the value you add is knowing what the algorithm did, curating the raw output by role, and tuning it for accessibility so an inspired palette becomes an intentional one.

Frequently Asked Questions

How do I extract a color palette from an image?

Upload the image to a palette tool (Coolors, Adobe Color, Canva, or a Color Thief demo), or run it through a library like Color Thief or Vibrant.js in code. The tool reads the pixels, groups similar colors together, and returns the handful of dominant colors as HEX or RGB values. Under the hood almost every one of these uses color quantization — usually k-means clustering or a median-cut algorithm — to reduce millions of pixel colors down to the 5-10 that best represent the image. You then pick which of those to keep as your primary, secondary, and accent colors.

What algorithm do color extractors use?

Most use color quantization. The two dominant approaches are k-means clustering (group pixels into k color clusters, take each cluster's average as one palette color) and median cut (recursively split the color space in half until you have the number of buckets you want, then average each bucket). Color Thief uses a variant of median cut called modified median cut quantization (MMCQ). Both aim to find colors that are visually representative, not just the most numerically frequent, which is why they down-weight near-neutral and off-screen colors.

How does k-means clustering extract colors?

K-means treats every pixel as a point in a 3D color space (its red, green, and blue values). You pick k — the number of colors you want. The algorithm places k starting centers, assigns each pixel to its nearest center, moves each center to the average of the pixels assigned to it, and repeats until the centers stop moving. The final k centers are your palette. More clusters means a more detailed palette; fewer means a more simplified one.

Can I extract colors from an image with JavaScript?

Yes. Draw the image to an HTML canvas, call getImageData() to read the raw RGBA pixel array, and either run your own quantization or hand the array to a library. Color Thief does exactly this in about two lines: new ColorThief().getPalette(imgElement, 8) returns eight dominant colors. Vibrant.js goes further and labels swatches by role (Vibrant, Muted, DarkVibrant, and so on). Both run entirely in the browser with no server round trip.

Why do different tools give different palettes from the same image?

Because they use different algorithms and different defaults. K-means starts from random centers, so two runs can even differ slightly. Median cut splits the color space differently than clustering. Tools also disagree on how aggressively to merge similar colors, how many colors to return by default, and whether to filter out near-white, near-black, and low-saturation pixels. None of these is more correct — they are optimizing for slightly different definitions of dominant.

How many colors should I extract from an image?

Five to eight is the practical sweet spot for a usable palette. Extract too few (two or three) and you lose the accent colors that give a scheme character; extract too many (fifteen-plus) and you get near-duplicates that are hard to build a design from. A common workflow is to extract eight, then curate down to a core of one or two dominant colors, two or three supporting colors, and one accent.

Are extracted color palettes accessible?

Not automatically. Extraction optimizes for what is visually dominant in the source image, not for contrast between text and background. Before shipping an extracted palette, check every text/background pairing against WCAG contrast ratios (4.5:1 for normal text, 3:1 for large text and UI components) and adjust the lightness of individual colors as needed. Treat the extracted palette as a starting point, then tune it for accessibility.

Can I extract colors without uploading my image to a website?

Yes. Client-side tools and libraries like Color Thief and Vibrant.js process the image entirely in your browser using the Canvas API, so the pixels never leave your device. Design software (Photoshop, Figma, Affinity) also samples colors locally. If a tool asks you to upload to its server and you care about privacy or copyright, prefer a client-side option instead.

color extractioncolor paletteimage analysisdesign tools