utilime

Small tools that finish the job.

Pick a colour out of an image

Point at a pixel and read its colour back. The value comes from the decoded image at full resolution, not from the copy scaled to fit your screen.

Drop a file hereor click to choose · it stays on your device

How it works

The notation 4:2:0 stamped on a JPEG describes how much colour the file actually holds. Read it as a block two rows tall and four samples wide: brightness is recorded at all eight positions, the two colour-difference signals at two positions along the top row, and at none along the bottom row, which reuses the row above it. One colour reading therefore covers each 2×2 group of pixels. cjpeg from libjpeg-turbo writes that layout by default, as -sample 2x2, and decoding upsamples the absent readings back into place. Three of every four pixels in such a file carry a colour that was interpolated rather than measured.

Across a flat area the interpolation costs nothing, since the neighbours it guesses from are the same colour anyway. Across an edge it costs a great deal. A red letter on white paper has one colour reading spanning both the ink and the paper, and the decoder then spreads a ramp between them, so a pixel one step inside the letter can come back as a pink that appears nowhere in the scene. Averaging a small spot instead of reading a lone pixel pulls the answer towards the colour a viewer would assign to that region, because a viewer is integrating over roughly the same neighbourhood.

Where the sample is taken from

The image above is drawn at whatever width the column allows, which for a photograph off a phone is a large reduction. Resampling mixes several source pixels into each displayed one, and those mixtures are colours the file never contained. Reading the canvas would hand back one of the inventions wherever detail is finer than the scale factor. So the crosshair is positioned on the scaled canvas, since that is the thing you can see and point at, and the coordinate it writes is converted into source-image pixels. The value in the result panel is read from the full decode at original resolution.

Why the average is taken in linear light

An 8-bit sRGB channel does not hold light in proportion to its number. It holds roughly the 1/2.2 power of it, a curve defined by IEC 61966-2-1 as a linear segment below 0.04045 and a 2.4 exponent above. Averaging those numbers directly is arithmetic on exponents, and it returns less light than the pixels emit. On a black and white checkerboard the byte mean of 0 and 255 is 128, or #808080. Undoing the transfer function gives 0 and 1, whose mean of 0.5 re-encodes to 188, or #bcbcbc. Sixty levels apart, from identical pixels.

Both figures appear in the result panel, so the size of the correction is visible on the picture in front of you rather than asserted here. Over a smooth surface the two agree within a level or two. Over high-contrast texture the gap runs to tens of levels, and the byte average is the darker one every time, a direction guaranteed by the shape of the curve rather than by luck.

A screenshot is exact, a photograph is lit

Those two sources answer different questions. A screenshot holds the values a renderer wrote, so picking from one recovers the author's hex exactly, provided it was saved in a lossless format and no display profile was baked in on the way out. A photograph holds the light that left a surface. A white page under a tungsten bulb records as orange; the same page in open shade records as blue. Both are correct measurements of a real thing, and neither is the colour anyone would name the page. Read a picked photographic colour as a measurement of that material under that illumination.

Every format the rest of the suite decodes is accepted here, including HEIC from an iPhone, and the decode runs as WebAssembly inside a Web Worker on your own machine. The picture goes nowhere, which the network panel will confirm: the codec chunk downloads once and nothing else moves.

Questions

Why is the hex different from the one in my design file?

Two likely causes. If the source is a photograph, you have measured the light coming off a surface, which carries the colour of whatever lit it. If the source is a JPEG, the colour channels were stored at reduced resolution and rebuilt by interpolation, so any pixel within a couple of steps of an edge is a guess the encoder made. A lossless screenshot of the artwork will give the original value back.

The colour under the crosshair looks slightly different from the reported hex. Which one is right?

The result panel. The caption reads the browser's own decode so it can update while you drag, and it is limited by whatever the browser did with the file's colour profile. The row is produced by the same wasm decoders the rest of the suite uses, over the full-resolution pixels, with the spot averaged in linear light. Where they disagree, trust the row.

Which spot size should I use?

One pixel for screenshots, flat graphics, icons and anything drawn rather than captured, where the value is exact and averaging would only blur two regions together. Five pixels for ordinary photographs, which is enough to outvote sensor noise and chroma interpolation. Fifteen for visibly textured material such as skin, fabric or a painted wall. Watch the ring: if it straddles an edge, the answer is a blend of two things and belongs to neither.

What happens on a transparent pixel?

An alpha row appears, showing the mean coverage over the spot. The channels are averaged with alpha as the weight, so a spot half over the edge of a cut-out reports the colour of the part that is present rather than drifting towards whatever bytes sit beneath alpha 0. If the whole spot is fully transparent there is no weight to apply, and the stored RGB is reported unweighted.

Is the image uploaded to read one pixel?

No. Open the Network tab and pick a colour: the codec chunk arrives once and no request carries your file. Decoding happens in a Web Worker in this tab, which is also why a 40-megapixel photo does not freeze the page while it is read.