Skip to content
HexSlate

Image Color Picker

Palette size

No image yet

Drop a photo, a screenshot or a logo above. It is decoded in this tab, it is never uploaded, and the dominant palette appears as soon as it decodes.

0 kept

Every value read here comes from the decoded file rather than from the picture on your screen, and those two stop agreeing the moment a photo is scaled to fit a column. The dominant palette beside the image is clustered rather than bucketed, which is why a picture with foliage in it does not come back as six near-identical browns. The file is decoded in this tab and no copy of it is kept.

k-means in OKLab, not buckets in sRGB

The palette is clustered perceptually. Every sampled pixel is converted to OKLab, where equal distances look like equal differences, and the swatches are the centres of the clusters found there. The common alternative is histogram bucketing: chop the red, green and blue channels into a few bins each, count, and return the fullest bins. sRGB's axes are not spaced the way vision is, so that does two wrong things at once. It splits one perceived color across neighbouring bins, because a bin edge falls in the middle of a sky rather than between the sky and the roof, and it merges colors that look nothing alike, because the dark end of every channel is compressed and a slab of visibly different dark greens and browns lands in one bin. Averaging those counts then returns a mid-tone that appears in no pixel of the photograph. That is the mechanism behind the six near-identical browns most free extractors give back for any picture with foliage in it.

k-means itself is three steps. Place k centres among the sampled colors, assign every sample to the centre nearest it, then move each centre to the mean of the samples that chose it. Repeat until no sample changes hands, which is Lloyd's algorithm, and the centres settle where the colors actually gather instead of on a grid somebody chose in advance.

The specifics decide the result. 24,000 pixels are sampled, with the stride made coprime to the image width so a repeating vertical structure such as a fence is not sampled every time or never. The starting centres are spread apart k-means++ style, so eight seeds do not all land in a sky covering 70% of the frame. Two centres closer than 0.008 in OKLab, about a quarter of a just-noticeable difference, are folded into one, which is why asking for 8 swatches can return 6 for an image that has 6 colors in it. The seed is fixed, so the same image returns the same palette every time.

Why there is a loupe

The pixel under your cursor is not the pixel you think you are clicking, unless the image is drawn 1:1. A 4000 pixel wide photo shown in a 900 pixel column is resampled by the browser to fit, so what you see at any point is a blend of several source pixels, and a blend invents colors: the boundary between a red roof and a blue sky is displayed as a row of purples that were never photographed. The loupe reads the decode instead: the 11 by 11 block around the cursor at full resolution, with imageSmoothingEnabled off, so each cell is one real pixel from the file and the cell inside the black and white ring at its centre is exactly the value that gets copied.

What a canvas read can tell you about color, and what it cannot

A canvas read returns the decoded sRGB value, not the color your monitor is emitting. The browser applies the image's embedded ICC profile while decoding and converts the result into sRGB for a default 2D context, per the HTML specification. So a file tagged Display P3, Adobe RGB or ProPhoto arrives here already converted, with anything outside sRGB brought to its nearest sRGB equivalent, and a file with no profile is assumed to be sRGB. A color-managed viewer sends that same file to a wide-gamut display in its own space, where it can look visibly more saturated than the hex here suggests. The hex is still the right answer for the web, where an untagged color means sRGB, and the wrong one for matching a print proof or a P3 asset: take those numbers from an application that keeps the source profile.

Common problems

  • The hex does not match what an image editor reports. Usually the profile, for the reason above: an editor with a working space other than sRGB reads the same bytes against a different definition of red, green and blue. Compare in sRGB before assuming either is wrong.
  • One color fills most of the palette. k-means allocates by area, and a UI screenshot is mostly one background. Crop to the part you care about and drop that.
  • The extracted color is close to your brand color but not equal to it. A centroid is the mean of a cluster, so it lands near the colors in it and rarely on any one of them. When you need the exact value, use the loupe and pick the pixel.
  • Nothing happens when a file is dropped. The browser could not decode it. Camera raw and, outside Safari, HEIC are the usual cases. Export a JPEG or PNG first.

Frequently asked questions

Is my image uploaded anywhere?

No. The file is decoded with createImageBitmap and drawn to a canvas in this tab, and every pixel read, conversion and cluster happens there. There is no server to send it to, and the only thing that ever enters the URL is the swatch count, as ?k=. The rest of the color tools work the same way.

Why is the palette different from another extractor's?

Because the space and the method differ, and both change the answer. A histogram bucketer in sRGB returns bin averages; this returns cluster centres in a perceptual space. Two k-means implementations also disagree when their seeding is random, which is why many extractors return a different palette on every reload of the same picture. This one uses a fixed seed and fixed tie-breaks, so it does not.

Which image formats can I drop?

Whatever your browser decodes: JPEG, PNG, WebP, GIF, AVIF, BMP, ICO and SVG in every current browser. An animated GIF or WebP is read as its first frame. Camera raw files are not images to a browser, and HEIC decodes only in Safari.

Does it work without a mouse?

Yes. The image is focusable, the arrow keys step the cursor one pixel and shift steps ten, Enter or Space keeps the color, and each move is announced with its coordinates, hex and nearest CSS color name through a live region. Every swatch on the page carries its hex as text rather than only as a fill, so nothing here depends on seeing the color.

What happens to transparency?

A picked pixel keeps its alpha, and the value shows it: a half-transparent pixel is not the color it appears to be over this page's background. The palette pass is stricter and skips anything below 50% opacity, because a fully transparent pixel is almost always transparent black, and a logo with a large clear margin would otherwise come back with black as its dominant color.

Does a very large photo slow it down?

Barely. The clustering cost is set by the 24,000 sample rather than by the file, so a 40-megapixel photo costs about what a 2-megapixel one does: roughly 70 ms for 8 swatches on a desktop, a few hundred milliseconds on a slow phone. Above 4 megapixels the image is redrawn smaller with smoothing off before the palette pass, so a phone is never asked for one enormous allocation. Picking is unaffected: the loupe and every picked pixel are read from the full-resolution decode.