The problem: 16 million colors, and we want eight

A normal photo can carry one of roughly 16.7 million colors per pixel — and often actually contains tens of thousands of them. The color palette tool is supposed to turn that into a handful of dominant, representative colors. The question is: which eight colors sum up an image best? The path there is called color quantization — and our variant is deliberately kept simple.

Step 1: read the image onto a canvas

First the image is drawn onto an (invisible) canvas, and via getImageData() we get the raw pixel data: one long array in which every four values describe a pixel — red, green, blue, and alpha. From here on, it's all just arithmetic with numbers. And like everything on our site, this runs locally in the browser; the image never leaves your machine (more on that in the workshop notes on the browser-local architecture).

Step 2: group similar colors together (the grid)

If you counted every exact color individually, you'd end up with thousands of nearly identical shades — useless for a palette. The trick is to throw similar colors into the same bucket. To do that, we round each of the three color channels to the nearest multiple of 16:

r = Math.round(r / 16) * 16;
g = Math.round(g / 16) * 16;
b = Math.round(b / 16) * 16;

That turns 256 possible values per channel into just 16 steps — and two shades of red that differ by only a few numeric values land in the same bucket. Every bucket gets a key like "128,64,32", and we count how often it gets hit.

300 × 250 — Rectangle
Cookie-Banner ausstehend

Step 3: don't read every pixel (subsampling)

One detail that makes the difference between "stutters" and "instant": we read not every single pixel, but only every n-th one (subsampling). A photo with millions of pixels doesn't need more — for the dominant colors, a representative sample is enough, and it's many times faster. The palette barely changes as a result, because frequent colors stay frequent in the sample too. On top of that, we downscale large images beforehand — even more speed, same result.

Step 4: sort by frequency

Now we have a list of color buckets with their hit counts. We sort them by frequency — the most frequently hit color first — and take the top ones as the palette. From the total number of hits we also compute each color's percentage share, so you can see how strongly a shade shapes the image. That's the palette done.

Why not the "better" algorithm?

There are more sophisticated approaches — median cut, k-means clustering — that deliver mathematically "more optimal" palettes. We deliberately chose the simple, frequency-based route, for three reasons:

  • Speed: the approach runs in milliseconds, even on weak devices — important when everything happens in the browser.
  • Explainability: "the most frequent colors" is instantly understandable to users; a cluster center that doesn't even appear in the image wouldn't be.
  • The good-enough principle: for design palettes, moodboards, and web colors, the frequency-based method delivers results that hold up in practice.

That's a recurring decision in this workshop: the simplest approach that solves the task well beats the theoretically best one when it needs to be local, fast, and understandable.

300 × 250 — Rectangle
Cookie-Banner ausstehend

What to do with the palette

The extracted colors are more than a gimmick: they provide the through-line for a moodboard, the foundation for a consistent graphic series, or the brand colors you orient your design around. And because the tool outputs the values as HEX, you can drop them straight into CSS or design apps.

Sources

MDN — getImageData() · Wikipedia — Color quantization.