Please bookmark this page to avoid losing your image tool!

Image Color Scheme Generator

(Free & Supports Bulk Upload)

Drag & drop your images here or

The result will appear here...
You can edit the below JavaScript code to customize the image tool.
function processImage(originalImg, numColors = 5) {
    // Parameter validation
    let k = parseInt(numColors, 10);
    if (isNaN(k) || k < 2) k = 5;
    k = Math.max(1, Math.min(k, 20)); // clamp between 1 and 20

    // Step 1: Downscale image for fast processing to extract colors
    const processSize = 64;
    const tempCanvas = document.createElement('canvas');
    tempCanvas.width = processSize;
    tempCanvas.height = processSize;
    const tempCtx = tempCanvas.getContext('2d');
    
    // Draw original image into small canvas
    tempCtx.drawImage(originalImg, 0, 0, processSize, processSize);
    const imgData = tempCtx.getImageData(0, 0, processSize, processSize).data;

    // Collect valid pixels (ignoring highly transparent ones)
    let pixels = [];
    for (let i = 0; i < imgData.length; i += 4) {
        if (imgData[i + 3] >= 128) { // alpha channel
            pixels.push({
                r: imgData[i],
                g: imgData[i + 1],
                b: imgData[i + 2]
            });
        }
    }

    // Fallback if image is fully transparent
    if (pixels.length === 0) {
        pixels.push({ r: 0, g: 0, b: 0 });
    }

    // Step 2: K-Means++ Initialization
    let centroids = [];
    centroids.push(pixels[Math.floor(Math.random() * pixels.length)]);

    while (centroids.length < k) {
        let maxDist = -1;
        let bestPixel = pixels[0];
        for (let i = 0; i < pixels.length; i++) {
            let p = pixels[i];
            let minDist = Infinity;
            for (let c of centroids) {
                let dist = (p.r - c.r) ** 2 + (p.g - c.g) ** 2 + (p.b - c.b) ** 2;
                if (dist < minDist) minDist = dist;
            }
            if (minDist > maxDist) {
                maxDist = minDist;
                bestPixel = p;
            }
        }
        centroids.push({ ...bestPixel });
    }

    // Step 3: K-Means Clustering (up to 10 iterations)
    for (let iter = 0; iter < 10; iter++) {
        let clusters = Array(k).fill(0).map(() => ({ r: 0, g: 0, b: 0, count: 0 }));

        for (let i = 0; i < pixels.length; i++) {
            let p = pixels[i];
            let minDist = Infinity;
            let minIdx = -1;
            for (let j = 0; j < k; j++) {
                let c = centroids[j];
                let dist = (p.r - c.r) ** 2 + (p.g - c.g) ** 2 + (p.b - c.b) ** 2;
                if (dist < minDist) {
                    minDist = dist;
                    minIdx = j;
                }
            }
            clusters[minIdx].r += p.r;
            clusters[minIdx].g += p.g;
            clusters[minIdx].b += p.b;
            clusters[minIdx].count++;
        }

        let changed = false;
        for (let j = 0; j < k; j++) {
            if (clusters[j].count > 0) {
                let newR = Math.round(clusters[j].r / clusters[j].count);
                let newG = Math.round(clusters[j].g / clusters[j].count);
                let newB = Math.round(clusters[j].b / clusters[j].count);
                
                if (newR !== centroids[j].r || newG !== centroids[j].g || newB !== centroids[j].b) {
                    changed = true;
                }
                centroids[j].r = newR;
                centroids[j].g = newG;
                centroids[j].b = newB;
            } else {
                // Handle empty clusters by re-assigning a random pixel
                centroids[j] = { ...pixels[Math.floor(Math.random() * pixels.length)] };
                changed = true;
            }
        }
        if (!changed) break;
    }

    // Step 4: Sort colors by perceptual luminance
    centroids.sort((a, b) => {
        let lumA = 0.299 * a.r + 0.587 * a.g + 0.114 * a.b;
        let lumB = 0.299 * b.r + 0.587 * b.g + 0.114 * b.b;
        return lumB - lumA; // Lightest to darkest
    });

    // Step 5: Construction of Visual Output Canvas
    const outCanvas = document.createElement('canvas');
    const outCtx = outCanvas.getContext('2d');

    const PADDING = 24;
    const MAX_IMG_WIDTH = 800;
    
    // Scale image to fit within max width while maintaining aspect ratio
    const scale = Math.min(1, MAX_IMG_WIDTH / originalImg.width);
    const imgDrawWidth = originalImg.width * scale;
    const imgDrawHeight = originalImg.height * scale;

    const paletteHeight = 100;
    const gap = 8; // gap between swatches

    outCanvas.width = imgDrawWidth + PADDING * 2;
    outCanvas.height = PADDING + imgDrawHeight + PADDING + paletteHeight + PADDING;

    // Background
    outCtx.fillStyle = '#1e1e1e';
    outCtx.fillRect(0, 0, outCanvas.width, outCanvas.height);

    // Draw Original Image
    outCtx.shadowColor = 'rgba(0,0,0,0.5)';
    outCtx.shadowBlur = 10;
    outCtx.drawImage(originalImg, PADDING, PADDING, imgDrawWidth, imgDrawHeight);
    
    // Reset shadow for swatches
    outCtx.shadowColor = 'transparent';
    outCtx.shadowBlur = 0;

    // Helper to convert RGB to HEX
    const rgbToHex = (r, g, b) => {
        return "#" + [r, g, b].map(x => {
            const hex = x.toString(16);
            return hex.length === 1 ? '0' + hex : hex;
        }).join('').toUpperCase();
    };

    // Draw Color Palette Swatches
    const swatchWidth = (imgDrawWidth - gap * (k - 1)) / k;
    const paletteY = PADDING + imgDrawHeight + PADDING;

    // Determine safe font size based on swatch layout
    const fontSize = Math.max(10, Math.min(16, swatchWidth / 5));

    for (let i = 0; i < k; i++) {
        let c = centroids[i];
        let hex = rgbToHex(c.r, c.g, c.b);
        let x = PADDING + i * (swatchWidth + gap);

        // Draw Swatch
        outCtx.fillStyle = hex;
        outCtx.fillRect(x, paletteY, swatchWidth, paletteHeight);

        // Determine Text Color (Black/White contrast based on luminance)
        let lum = 0.299 * c.r + 0.587 * c.g + 0.114 * c.b;
        outCtx.fillStyle = lum > 140 ? '#000000' : '#FFFFFF';

        // Draw Hex Text
        outCtx.font = `bold ${fontSize}px sans-serif`;
        outCtx.textAlign = 'center';
        outCtx.textBaseline = 'middle';
        
        // Prevent text from overflowing its box drastically
        if (swatchWidth > 40) {
            outCtx.fillText(hex, x + swatchWidth / 2, paletteY + paletteHeight / 2);
        }
    }

    return outCanvas;
}

Free Image Tool Creator

Can't find the image tool you're looking for?
Create one based on your own needs now!

Description

The Image Color Scheme Generator extracts a harmonious color palette from any uploaded image. By using advanced clustering algorithms, it identifies the most dominant colors and presents them as a visual palette with their corresponding HEX codes. This tool is highly useful for graphic designers, web developers, and digital artists who need to find consistent color schemes for branding, UI design, or art projects based on existing visual inspiration.

Leave a Reply

Your email address will not be published. Required fields are marked *