You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(originalImg, numColors = 5, nameLength = 2) {
/**
* Helper function to convert RGB color values to HSL.
* @param {number} r - Red value (0-255)
* @param {number} g - Green value (0-255)
* @param {number} b - Blue value (0-255)
* @returns {Array<number>} An array containing [hue, saturation, lightness]
*/
function rgbToHsl(r, g, b) {
r /= 255;
g /= 255;
b /= 255;
const max = Math.max(r, g, b),
min = Math.min(r, g, b);
let h = 0,
s, l = (max + min) / 2;
if (max === min) {
h = s = 0; // achromatic
} else {
const d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch (max) {
case r:
h = (g - b) / d + (g < b ? 6 : 0);
break;
case g:
h = (b - r) / d + 2;
break;
case b:
h = (r - g) / d + 4;
break;
}
h /= 6;
}
return [h * 360, s, l];
}
/**
* Helper function to convert RGB to a CSS hex string.
* @param {number} r - Red value
* @param {number} g - Green value
* @param {number} b - Blue value
* @returns {string} The hex color string (e.g., "#FF5733").
*/
function rgbToHex(r, g, b) {
return "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1).toUpperCase();
}
/**
* Gets a descriptive adjective based on a color's lightness and saturation.
* @param {number} l - Lightness (0-1)
* @param {number} s - Saturation (0-1)
* @returns {string} A descriptive adjective.
*/
function getAdjective(l, s) {
if (s < 0.1) {
if (l > 0.8) return "Cloudy";
if (l < 0.2) return "Graphite";
return "Stone";
}
if (l > 0.85) return "Pale";
if (l < 0.20) return "Deep";
if (s < 0.35) return "Misty";
if (s > 0.80) return "Vibrant";
if (l > 0.70) return "Bright";
if (l < 0.35) return "Dark";
return "Golden"; // A pleasant default
}
/**
* Gets a descriptive noun based on a color's hue.
* @param {number} h - Hue (0-360)
* @returns {string} A descriptive noun.
*/
function getNoun(h) {
if (h >= 340 || h < 15) return "Rose";
if (h < 35) return "Sunset";
if (h < 60) return "Buttercup";
if (h < 75) return "Daffodil";
if (h < 100) return "Lime";
if (h < 140) return "Forest";
if (h < 160) return "Mint";
if (h < 190) return "Seafoam";
if (h < 220) return "Ocean";
if (h < 260) return "Twilight";
if (h < 280) return "Indigo";
if (h < 310) return "Violet";
if (h < 340) return "Magenta";
return "Garnet";
}
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d', {
willReadFrequently: true
});
// 1. Downsample the image for faster processing
const MAX_DIMENSION = 100;
let width = originalImg.width;
let height = originalImg.height;
if (width > height) {
if (width > MAX_DIMENSION) {
height *= MAX_DIMENSION / width;
width = MAX_DIMENSION;
}
} else {
if (height > MAX_DIMENSION) {
width *= MAX_DIMENSION / height;
height = MAX_DIMENSION;
}
}
canvas.width = Math.max(1, width);
canvas.height = Math.max(1, height);
ctx.drawImage(originalImg, 0, 0, canvas.width, canvas.height);
// 2. Analyze pixels and quantize colors to find dominant ones
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height).data;
const colorMap = {};
const quantizeShift = 4; // Bit shift to reduce 256^3 colors to 16^3
for (let i = 0; i < imageData.length; i += 4) { // Iterate through pixels (R,G,B,A)
const r = imageData[i];
const g = imageData[i + 1];
const b = imageData[i + 2];
const alpha = imageData[i + 3];
if (alpha < 128) continue; // Ignore transparent pixels
if (r > 245 && g > 245 && b > 245) continue; // Ignore white
if (r < 10 && g < 10 && b < 10) continue; // Ignore black
// Create a single key for a quantized color
const key = (r >> quantizeShift) << 10 | (g >> quantizeShift) << 5 | (b >> quantizeShift);
if (!colorMap[key]) {
colorMap[key] = { r: 0, g: 0, b: 0, count: 0 };
}
colorMap[key].r += r;
colorMap[key].g += g;
colorMap[key].b += b;
colorMap[key].count++;
}
// 3. Sort colors by frequency and calculate average for dominant ones
const sortedColorKeys = Object.keys(colorMap).sort((a, b) => colorMap[b].count - colorMap[a].count);
const dominantColors = [];
for (let i = 0; i < Math.min(sortedColorKeys.length, numColors); i++) {
const key = sortedColorKeys[i];
const colorData = colorMap[key];
dominantColors.push({
r: Math.round(colorData.r / colorData.count),
g: Math.round(colorData.g / colorData.count),
b: Math.round(colorData.b / colorData.count),
});
}
// Handle cases where no suitable colors are found
if (dominantColors.length === 0) {
const errorDiv = document.createElement('div');
errorDiv.textContent = "Could not generate a name. Image may be transparent or monochrome.";
return errorDiv;
}
// 4. Generate the name based on dominant colors
const nameParts = [];
const firstColor = dominantColors[0];
const firstHsl = rgbToHsl(firstColor.r, firstColor.g, firstColor.b);
nameParts.push(getAdjective(firstHsl[2], firstHsl[1]));
const secondColor = dominantColors.length > 1 ? dominantColors[1] : firstColor;
const secondHsl = rgbToHsl(secondColor.r, secondColor.g, secondColor.b);
nameParts.push(getNoun(secondHsl[0]));
if (nameLength > 2 && dominantColors.length > 2) {
const thirdColor = dominantColors[2];
const thirdHsl = rgbToHsl(thirdColor.r, thirdColor.g, thirdColor.b);
nameParts.push(getNoun(thirdHsl[0]));
}
const finalName = nameParts.slice(0, Math.max(1, nameLength)).join(" ");
// 5. Create the output HTML element
const container = document.createElement('div');
container.style.fontFamily = "Arial, sans-serif";
container.style.textAlign = "center";
container.style.padding = "20px";
container.style.border = "1px solid #ddd";
container.style.borderRadius = "8px";
container.style.backgroundColor = "#f9f9f9";
container.style.maxWidth = "400px";
container.style.margin = "0 auto";
container.style.boxShadow = "0 4px 8px rgba(0,0,0,0.05)";
const nameElement = document.createElement('h2');
nameElement.textContent = finalName;
nameElement.style.margin = "0 0 15px 0";
nameElement.style.color = "#333";
nameElement.style.fontWeight = "bold";
nameElement.style.textTransform = "capitalize";
const paletteContainer = document.createElement('div');
paletteContainer.style.display = "flex";
paletteContainer.style.justifyContent = "center";
paletteContainer.style.gap = "10px";
paletteContainer.style.flexWrap = "wrap";
dominantColors.forEach(color => {
const hex = rgbToHex(color.r, color.g, color.b);
const colorSwatch = document.createElement('div');
colorSwatch.style.width = "50px";
colorSwatch.style.height = "50px";
colorSwatch.style.backgroundColor = hex;
colorSwatch.style.borderRadius = "50%";
colorSwatch.style.border = "2px solid #fff";
colorSwatch.style.boxShadow = "0 2px 4px rgba(0,0,0,0.1)";
colorSwatch.title = `Dominant Color: ${hex}`;
paletteContainer.appendChild(colorSwatch);
});
container.appendChild(nameElement);
container.appendChild(paletteContainer);
return container;
}
Apply Changes