The picture is analysed on your machine
The file is decoded by the browser, drawn small into a canvas, and the pixel array is read back and clustered in JavaScript. All local. No request goes out when you pick the file and none goes out when the palette appears — check the network tab, or disconnect and extract a palette anyway.
How the colours are chosen
The image is first reduced so the long edge is 120 pixels, which is small enough to process instantly and large enough to preserve every colour region that matters. Every remaining pixel becomes a point in RGB space.
Median cut then treats those points as one box and repeatedly splits the box that is widest in any single channel, cutting at the median so each half holds the same number of pixels. Repeat until you have as many boxes as you asked for colours, average each box, and you have a palette. It is the algorithm that generated GIF palettes for decades, and it is stable in a way that sampling is not.
A few passes of k-means follow: each pixel is reassigned to whichever centre it is now closest to, and the centres are recomputed. That cleans up the boundaries median cut leaves behind when a split falls awkwardly through a gradient. The percentage beside each swatch is the share of analysed pixels that ended up assigned to it.
Using the result
The largest share is your background or base. The most saturated is usually the right accent for buttons and links, even when it occupies two percent of the frame — visual weight and pixel count are different things.
For text over the image, read the average luminance. Below about 0.18 relative luminance, white text clears the 4.5:1 contrast ratio; above roughly 0.4, black does. Those are averages over the whole frame, so a picture with a bright sky and a dark foreground can satisfy neither where the text actually sits. Check the specific region with the contrast checker, or put a translucent scrim behind the text and stop guessing.
The CSS block pastes straight into a stylesheet and gives you var(--color-1) through var(--color-6). Rename them to something meaningful before they spread through a codebase — numbered colour variables become unmaintainable the moment the palette changes.
When the palette looks wrong
A photograph with a wide white background or heavy shadows will spend half its palette on greys, because greys genuinely are half the image. The skip option removes near-white and near-black pixels from the clustering and gives the remaining colours the room, at the cost of no longer describing the picture faithfully. Use it for pulling a scheme out of a product shot on white; leave it off when you want to know what the image is.
Asking for more colours finds smaller accents but also splits similar tones into near-duplicates. A logo with four flat colours should be asked for four. Gradients are the awkward case: averaging across a smooth ramp invents an intermediate colour that is not literally present anywhere, which is correct behaviour and still surprising. And anything occupying one or two pixels of the original disappears in the reduction to 120 px, so a tiny bright detail will not show up.
The limits of an automatic palette
What comes out is a description, not a design. The frequencies in a photograph reflect how much area each colour covers, and area is not importance — a sky is most of the pixels in most outdoor photographs and is rarely the subject. Treat the output as a starting set to adjust: pull the accent's saturation up, push the base's down, and check the pairs you intend to put together for contrast before any of it reaches a stylesheet.
Questions people ask
Is each HEX a colour that exists in the image?
Not necessarily. Every swatch is the mean of a cluster of pixels, so on a smooth gradient the mean lands between the colours that are actually there. That is the point — a representative colour rather than an arbitrary sample. When you need the exact value of one specific spot, use the eyedropper in an image editor or your browser dev tools instead.
What determines the order?
Share of analysed pixels, largest first. That ordering answers "what is this image mostly made of" and deliberately does not answer "which colour should I use as the accent" — a small vivid colour will sit near the end of the list. The most saturated entry is called out separately for exactly that reason.
Does the skip option change the average colour?
No. The average and the luminance reading are always computed across every opaque pixel, so they keep describing the real image. Only the palette clustering respects the option. That is intentional: if the option changed both, you would lose any honest reading of how bright the picture is once you turned it on.
Why does my transparent PNG give odd colours?
Pixels with less than half alpha are skipped, but a partly transparent pixel is included at its stored colour, which may be nothing like what it looks like composited over a background. Anti-aliased edges are the usual source. If the palette has entries you cannot find in the picture, flatten the image onto its intended background first and extract from that.