Generating a Color Spectrum from an Image
Key point
A writeup experimenting with several sorting and bucketing strategies to visualize an image's color distribution.
Details
Spectrimage's goal was to show every color in a photo. Not just a few representative colors, but a visualization that also reveals the various tones of orange, the brightness variation of purple, and the intensity of green.
Initially, median cut quantization was used, but since it divides colors into equal-sized swatches, frequency information was lost and order got scrambled. It works for compression, but wasn't suitable for showing color distribution.
Next came a switch to HSL hue histogram. The color wheel order was preserved, and since each bin's width reflected pixel count, frequency showed up well. But 5-degree bins were too coarse, so brightness differences within the same hue got mixed into a single block, creating muddy colors.
An attempt was made to sort at the pixel level to create a denser spectrum, but sorting by hue alone caused brightness patterns to cross within the same hue, creating stripes. Even adding lightness sorting within each ROYGBIV segment, or doing degree-level grouping, the discontinuity of reverting to dark colors at each boundary kept repeating.
Rendering with Canvas instead of the DOM, and smoothing with the average of neighboring pixels, was tried, but as long as 2D information is compressed into a 1D order, the banding artifact wouldn't disappear. Eventually the approach changed: instead of forcing brightness differences to flatten into a single line, they were raised onto a second axis.
In the final design:
- x-axis: hue arranged in ROYGBIV order
- y-axis: lightness, with tint expressed upward and shade downward
- bar height: pixel count for that hue
This way, each hue's presence, proportion, and tone range can be read at a glance. The result looks wave-like, and in the example image, the orange region rises high, green appears thin, and magenta clusters at the purple end.
Black-and-white photos are handled separately. If 95% or more of all pixels are achromatic, the axes are swapped, putting lightness on the x-axis and rendering it divided into 60 bins from black to white. This way, images with different tonal characteristics, like a foggy landscape versus a high-contrast portrait, are expressed as distinct silhouettes.
The implementation runs entirely client-side. The image is downscaled to a hidden canvas based on a 300px long edge, drawn, then every pixel is read and converted to HSL. Colorless pixels go into the achromatic bucket, and the rest are classified into 2-degree hue bins.
Within each hue bin, after sorting by lightness:
- the bottom 20% as shade
- the middle 20% as pure color
- the top 20% as tint
are averaged to produce the representative color. This choice reduces noise from extreme values. Finally, the bins are rearranged in ROYGBIV order, with the achromatic bin appended at the end, and drawn on an HTML Canvas. It's noted that even a 4000x3000 photo is analyzed in under 1 second.
This summary was generated automatically by AI. Check the original for the author's claims and context. Copyright belongs to the original author.
Our guide explains how the AI works. Report summary errors, attribution issues, or removal requests via Contact.