Please bookmark this page to avoid losing your image tool!

Image Theme Creator Powered By AI

(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.
/**
 * Creates a theme (a color palette) from an image by finding the dominant colors.
 * This function uses a k-means clustering algorithm to identify the most representative
 * colors in the source image, simulating an "AI" powered theme creator.
 * The result is a canvas displaying the original image with the generated color
 * palette swatches and their hex codes underneath.
 *
 * @param {HTMLImageElement} originalImg The source image object.
 * @param {number} [paletteSize=5] The number of dominant colors to extract for the theme palette.
 * @returns {Promise<HTMLCanvasElement>} A canvas element containing the original image and the generated color theme palette.
 */
async function processImage(originalImg, paletteSize = 5) {
    // Ensure paletteSize is a valid positive integer
    const k = Math.max(1, parseInt(paletteSize, 10) || 5);

    // --- 1. Get Pixel Data ---
    // Use a temporary canvas to draw the image and extract its pixel data.
    const tempCanvas = document.createElement('canvas');
    const tempCtx = tempCanvas.getContext('2d', { willReadFrequently: true });
    tempCanvas.width = originalImg.naturalWidth;
    tempCanvas.height = originalImg.naturalHeight;
    tempCtx.drawImage(originalImg, 0, 0);

    let imageData;
    try {
        imageData = tempCtx.getImageData(0, 0, tempCanvas.width, tempCanvas.height);
    } catch (e) {
        console.error("Could not get image data. The image might be cross-origin.", e);
        // Create a fallback canvas with an error message
        const errorCanvas = document.createElement('canvas');
        errorCanvas.width = originalImg.width;
        errorCanvas.height = 100;
        const errorCtx = errorCanvas.getContext('2d');
        errorCtx.fillStyle = '#f0f0f0';
        errorCtx.fillRect(0, 0, errorCanvas.width, errorCanvas.height);
        errorCtx.fillStyle = '#ff0000';
        errorCtx.font = '16px Arial';
        errorCtx.textAlign = 'center';
        errorCtx.fillText('Error: Could not process image.', errorCanvas.width / 2, 40);
        errorCtx.fillText('Image may be from another domain (CORS issue).', errorCanvas.width / 2, 60);
        return errorCanvas;
    }
    
    const pixels = imageData.data;
    const pixelArray = [];
    // Create an array of [R, G, B] values, ignoring transparent pixels
    for (let i = 0; i < pixels.length; i += 4) {
        if (pixels[i + 3] > 128) { // Alpha channel check
            pixelArray.push([pixels[i], pixels[i + 1], pixels[i + 2]]);
        }
    }
    
    // If image is fully transparent or empty, return a default palette
    if (pixelArray.length === 0) {
        pixelArray.push([255, 255, 255], [221, 221, 221], [170, 170, 170], [85, 85, 85], [0, 0, 0]);
    }
    
    /**
     * --- 2. K-Means Clustering Algorithm ---
     * This algorithm finds 'k' cluster centers (centroids) in the pixel color data.
     * These centroids represent the dominant colors in the image.
     */
    function getDominantColors(pixels, k) {
        // Use a subset of pixels for performance on large images
        const sample = pixels.length > 20000 ? pixels.filter((_, i) => i % Math.floor(pixels.length / 20000) === 0) : pixels;
        
        // Helper to calculate squared distance (faster than Euclidean)
        const distance = (a, b) => (a[0] - b[0]) ** 2 + (a[1] - b[1]) ** 2 + (a[2] - b[2]) ** 2;

        // Initialize centroids by picking random pixels from the sample
        let centroids = [];
        const usedIndices = new Set();
        while (centroids.length < k && centroids.length < sample.length) {
            const index = Math.floor(Math.random() * sample.length);
            if (!usedIndices.has(index)) {
                centroids.push([...sample[index]]);
                usedIndices.add(index);
            }
        }

        const maxIterations = 20;
        for (let iter = 0; iter < maxIterations; iter++) {
            const clusters = Array.from({ length: k }, () => []);
            // Assign each pixel to the closest centroid
            for (const pixel of sample) {
                let minDistance = Infinity;
                let closestCentroidIndex = 0;
                for (let i = 0; i < k; i++) {
                    const d = distance(pixel, centroids[i]);
                    if (d < minDistance) {
                        minDistance = d;
                        closestCentroidIndex = i;
                    }
                }
                clusters[closestCentroidIndex].push(pixel);
            }

            let hasConverged = true;
            const newCentroids = [];
            // Recalculate centroids
            for (let i = 0; i < k; i++) {
                if (clusters[i].length === 0) {
                    // If a cluster is empty, re-initialize its centroid to a random pixel
                    newCentroids.push(sample[Math.floor(Math.random() * sample.length)]);
                    hasConverged = false;
                    continue;
                }
                const sum = clusters[i].reduce((acc, p) => [acc[0] + p[0], acc[1] + p[1], acc[2] + p[2]], [0, 0, 0]);
                const avg = sum.map(c => Math.round(c / clusters[i].length));
                
                if (distance(avg, centroids[i]) > 1) {
                    hasConverged = false;
                }
                newCentroids.push(avg);
            }

            centroids = newCentroids;
            if (hasConverged) break;
        }

        return centroids;
    }

    const dominantColors = getDominantColors(pixelArray, k);

    // --- 3. Create Output Canvas ---
    const swatchHeight = Math.max(60, Math.min(100, originalImg.naturalWidth / k));
    const outputCanvas = document.createElement('canvas');
    const ctx = outputCanvas.getContext('2d');
    outputCanvas.width = originalImg.naturalWidth;
    outputCanvas.height = originalImg.naturalHeight + swatchHeight;

    // Draw original image
    ctx.drawImage(originalImg, 0, 0);

    // Draw color palette swatches
    const swatchWidth = outputCanvas.width / dominantColors.length;

    // Helper functions for drawing
    const rgbToHex = (r, g, b) => '#' + [r, g, b].map(x => x.toString(16).padStart(2, '0')).join('');
    const getTextColor = (r, g, b) => (0.299 * r + 0.587 * g + 0.114 * b) > 128 ? '#000000' : '#FFFFFF';

    dominantColors.forEach((color, index) => {
        const [r, g, b] = color;
        const hex = rgbToHex(r, g, b);
        const x = index * swatchWidth;
        const y = originalImg.naturalHeight;

        // Draw color swatch
        ctx.fillStyle = `rgb(${r}, ${g}, ${b})`;
        ctx.fillRect(x, y, swatchWidth, swatchHeight);

        // Draw hex code on top
        ctx.fillStyle = getTextColor(r, g, b);
        const fontSize = Math.max(12, Math.min(18, swatchWidth / 6));
        ctx.font = `${fontSize}px Arial`;
        ctx.textAlign = 'center';
        ctx.textBaseline = 'middle';
        ctx.fillText(hex.toUpperCase(), x + swatchWidth / 2, y + swatchHeight / 2);
    });

    return outputCanvas;
}

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 Theme Creator Powered by AI is a tool designed to extract dominant colors from an image and generate a cohesive color palette. By utilizing a k-means clustering algorithm, the tool identifies the most representative colors in the uploaded image and displays them along with their hex codes. This can be particularly useful for designers seeking color inspiration for web or graphic projects, developers looking to create consistent color themes for applications, or individuals wanting to select colors for home decor based on a favorite image. The tool provides a visual reference of the colors derived from your image, facilitating easy and effective color selection.

Leave a Reply

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