How to Auto-Extract Theme Colors from Images with JavaScript: Median‑Cut & Octree Explained
This article explains how to automatically extract a picture’s dominant theme colors in the browser using JavaScript, covering the problem background, analysis of common methods, advantages of client‑side processing, and step‑by‑step implementation of median‑cut and octree algorithms with code examples.
1. Introduction
Theme color is the primary color used in UI design to set the overall atmosphere and style. Proper selection improves visibility, usability and user experience.
2. Background
WeChat mini‑program CRM membership module supports level and rights cards. Merchants can customize card appearance (medal, background, images, text color) but must manually set background/theme colors, which is costly and limited to a few preset palettes.
Extracting theme colors is often associated with OpenCV, yet JavaScript can also accomplish it with real‑time, browser‑native capabilities.
3. Requirement Analysis
To automate theme‑color extraction we consider three common approaches:
Average‑color method – simple but may miss dominant hues.
Color histogram – selects histogram peaks; works for multi‑color images but overall quality is limited.
OpenCV – powerful but requires server‑side processing.
JavaScript offers client‑side control, responsiveness for small images, and interactive adjustments.
4. Technical Solution
The solution consists of three steps:
Load image data using Canvas and getImageData (optionally in a Web Worker).
Pre‑process the pixel data: denoise (Gaussian filter) and peel off near‑white/black borders.
Generate theme colors using either median‑cut or octree algorithms.
4.1 Load Image Data
Create a Canvas element, draw the image with
drawImage, then obtain an ImageData object via
getImageData. Web Workers are used for off‑screen rendering to avoid blocking the main thread.
4.2 Data Pre‑processing
4.2.1 Denoising
Apply Gaussian filter (convolution) to remove noise. Convolution is explained with examples such as image blurring, sharpening, edge detection, and even magic‑wand selection.
4.2.2 Peeling
Remove pixels whose RGB values are close to pure white or black (e.g.,
if (!(Math.min(r,g,b) >= max || Math.max(r,g,b) <= min)) { … }).
<code>function convertToPixelsArray(imgData) {
const data = imgData.data;
const pixels = [];
const [min, max] = [5, 250]; // filter range can be adjusted
for (let i = 0; i < data.length; i += 4) {
const r = data[i];
const g = data[i + 1];
const b = data[i + 2];
if (!(Math.min(r, g, b) >= max || Math.max(r, g, b) <= min)) {
// optional: filter alpha channel
pixels.push([data[i], data[i + 1], data[i + 2]]);
}
}
return pixels;
}
</code>4.3 Generate Theme Colors
Two algorithms are used:
Median‑cut
Pixels are treated as points in a 3‑D RGB cube. The algorithm repeatedly splits the box with the longest side at its median until a desired number of boxes is reached, then averages the colors.
<code>function medianCut(pixels) {
const colorRange = getColorRange(pixels); // RGB range of the box
const colorBox = new ColorBox(colorRange, pixels.length, pixels);
const divisions = 8; // maximum cuts, can be customized
let [box1, box2] = cutBox(colorBox); // first split
return queueCut([box1, box2], divisions);
}
function queueCut(queue, num) {
let isSmallBox = false;
while (queue.length < num && !isSmallBox) {
const denselyBox = queue.shift();
const resultBox = cutBox(denselyBox);
if (resultBox.length === 1) {
isSmallBox = true;
queue.unshift(resultBox);
return;
}
queue = insertValueInSortedArray(queue, resultBox); // insertion sort by density
}
return queue;
}
function cutBox(colorBox) {
const { colorRange, total, rgbArr } = colorBox;
const cutSide = colorBox.getCutSide(colorRange);
if (cutSide === -1) return colorBox; // stop when box is too small
const colorInCutSide = rgbArr.map(item => item[cutSide]);
let medianColor = getMedianColor(colorInCutSide, total);
if (medianColor === colorRange[cutSide][0]) {
medianColor++;
}
const newRange = getCutRange(colorRange, cutSide, medianColor);
const dividedPixels = dividePixel(rgbArr, cutSide, medianColor);
return [boxOne, boxTwo];
}
</code>Octree
The octree recursively divides the RGB space into eight sub‑cubes. Pixels are inserted; when a leaf node exceeds a threshold
N, its parent is merged to limit memory usage. After all pixels are processed, the
Nleaves with the most pixels provide the dominant colors.
5. Results
Using Canvas and the described algorithms, theme colors can be extracted in real time on the front end, applied to membership cards for automatic color matching, and used for gradient placeholders during image loading. The following screenshots show the before/after effects on rights and level cards.
When loading large images, the extracted theme color can be used for a blurred gradient placeholder, improving perceived performance.
6. References
Median‑cut – https://www.jianshu.com/p/a2cc58eba182
Octree – https://sunjiadai.xyz/blog/2019/06/25/OcTree%E5%85%AB%E5%8F%89%E6%A0%91/
Wei‑dong, C., & Wei, D. (2008). An improved median‑cut algorithm of color image quantization. IEEE.
Yamaguchi, K., Kunii, T., Fujimura, K., & Toriya, H. (1984). Octree‑related data structures and algorithms. IEEE Computer Graphics & Applications, 4(1), 53‑59.
Weimob Technology Center
Official platform of the Weimob Technology Center
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.