You can edit the below JavaScript code to customize the image tool.
Apply Changes
/**
* Helper function to convert an RGB color value to HSL. This is used to determine the "mood" from the logo's average color.
* @param {number} r The red color value [0, 255]
* @param {number} g The green color value [0, 255]
* @param {number} b The blue color value [0, 255]
* @returns {Array<number>} An array containing the HSL values [hue, saturation, lightness].
*/
function rgbToHsl(r, g, b) {
r /= 255, g /= 255, b /= 255;
let max = Math.max(r, g, b), min = Math.min(r, g, b);
let h = 0, s = 0, l = (max + min) / 2;
if (max !== min) {
let 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 * 100, l * 100];
}
/**
* Creates a visual comparison between a logo and a "musical interpretation" based on the logo's visual properties.
* @param {Image} originalImg The input Image object (the logo).
* @param {string} backgroundColor The background color of the output canvas.
* @param {string} textColor The color for text and borders on the canvas.
* @returns {HTMLCanvasElement} A canvas element containing the side-by-side comparison and analysis.
*/
function processImage(originalImg, backgroundColor = '#1a1a1a', textColor = '#ffffff') {
// 1. Setup Canvas
const padding = 40;
const spacing = 40;
const textHeight = 120;
const canvas = document.createElement('canvas');
canvas.width = originalImg.width * 2 + padding * 2 + spacing;
canvas.height = originalImg.height + padding * 2 + textHeight;
const ctx = canvas.getContext('2d');
// Fill background
ctx.fillStyle = backgroundColor;
ctx.fillRect(0, 0, canvas.width, canvas.height);
// 2. Analyze Image to get average color and brightness
const tempCanvas = document.createElement('canvas');
tempCanvas.width = originalImg.width;
tempCanvas.height = originalImg.height;
// Use willReadFrequently hint for potential performance improvement
const tempCtx = tempCanvas.getContext('2d', { willReadFrequently: true });
tempCtx.drawImage(originalImg, 0, 0);
let imageData;
try {
imageData = tempCtx.getImageData(0, 0, originalImg.width, originalImg.height);
} catch (e) {
// Handle potential security errors (tainted canvas) if the image is from a different origin
ctx.fillStyle = textColor;
ctx.textAlign = 'center';
ctx.font = '16px Arial, sans-serif';
ctx.fillText('Could not process image due to cross-origin restrictions.', canvas.width / 2, canvas.height / 2);
console.error("Canvas Tainted:", e);
return canvas;
}
const data = imageData.data;
let totalR = 0, totalG = 0, totalB = 0, totalBrightness = 0;
const pixelCount = originalImg.width * originalImg.height;
for (let i = 0; i < data.length; i += 4) {
const r = data[i];
const g = data[i + 1];
const b = data[i + 2];
totalR += r;
totalG += g;
totalB += b;
totalBrightness += (r + g + b) / 3;
}
const avgR = Math.round(totalR / pixelCount);
const avgG = Math.round(totalG / pixelCount);
const avgB = Math.round(totalB / pixelCount);
const avgBrightness = Math.round(totalBrightness / pixelCount);
const avgColorHex = `#${avgR.toString(16).padStart(2, '0')}${avgG.toString(16).padStart(2, '0')}${avgB.toString(16).padStart(2, '0')}`;
// 3. Draw Logo
const logoX = padding;
const logoY = padding;
ctx.drawImage(originalImg, logoX, logoY, originalImg.width, originalImg.height);
ctx.strokeStyle = textColor;
ctx.lineWidth = 1;
ctx.strokeRect(logoX - 1, logoY - 1, originalImg.width + 2, originalImg.height + 2);
// 4. Generate and Draw "Music" Visualization
const vizX = logoX + originalImg.width + spacing;
const vizY = logoY;
const vizWidth = originalImg.width;
const vizHeight = originalImg.height;
ctx.strokeRect(vizX - 1, vizY - 1, vizWidth + 2, vizHeight + 2);
ctx.save();
ctx.beginPath();
ctx.rect(vizX, vizY, vizWidth, vizHeight);
ctx.clip(); // Clip drawing to the visualization box
const centerY = vizY + vizHeight / 2;
const amplitude = (avgBrightness / 255) * (vizHeight * 0.4);
// Use average colors to create a unique and complex waveform
const freqR = (avgR / 255) * 4 + 1;
const freqG = (avgG / 255) * 4 + 2;
const freqB = (avgB / 255) * 4 + 3;
const pseudoRandom = (avgR + avgG + avgB) % 17 / 17;
ctx.lineWidth = 2;
ctx.strokeStyle = avgColorHex;
ctx.beginPath();
ctx.moveTo(vizX, centerY);
for (let x = 0; x < vizWidth; x++) {
const angle = (x / vizWidth) * Math.PI * 6; // Three full waves
const yOffset =
(Math.sin(angle * freqR + pseudoRandom) * (avgR / 255) +
Math.cos(angle * freqG + pseudoRandom) * (avgG / 255) +
Math.sin(angle * freqB + pseudoRandom) * (avgB / 255)) / 3;
const y = centerY + yOffset * amplitude;
ctx.lineTo(vizX + x, y);
}
ctx.stroke();
ctx.restore();
// 5. Draw Text Analysis
ctx.fillStyle = textColor;
ctx.font = 'bold 16px "Segoe UI", Arial, sans-serif';
ctx.textAlign = 'center';
const textRegionY = logoY + originalImg.height + 40;
const logoTextX = logoX + originalImg.width / 2;
const musicTextX = vizX + vizWidth / 2;
ctx.fillText('Logo Analysis', logoTextX, textRegionY);
ctx.fillText('Music Interpretation', musicTextX, textRegionY);
ctx.font = '14px "Segoe UI", Arial, sans-serif';
const line1Y = textRegionY + 30;
const line2Y = textRegionY + 55;
const line3Y = textRegionY + 80;
// Logo Analysis Details
ctx.textAlign = 'left';
ctx.fillText('Avg. Color:', logoX, line1Y);
ctx.fillStyle = avgColorHex;
ctx.fillRect(logoX + 90, line1Y - 12, 50, 16);
ctx.fillStyle = textColor;
ctx.fillText(avgColorHex.toUpperCase(), logoX + 150, line1Y);
ctx.fillText(`Brightness: ${Math.round(avgBrightness / 255 * 100)}%`, logoX, line2Y);
// Music Interpretation Details
const [hue] = rgbToHsl(avgR, avgG, avgB);
let mood = "Balanced";
if (hue >= 0 && hue < 30) mood = "Passionate"; else if (hue < 60) mood = "Joyful";
else if (hue < 90) mood = "Fresh"; else if (hue < 150) mood = "Natural";
else if (hue < 210) mood = "Serene"; else if (hue < 270) mood = "Calm";
else if (hue < 330) mood = "Royal"; else mood = "Passionate";
let tempo = "Andante (moderate)";
if (avgBrightness < 85) tempo = "Largo (slow)";
if (avgBrightness > 170) tempo = "Presto (fast)";
const colorRange = Math.max(avgR, avgG, avgB) - Math.min(avgR, avgG, avgB);
let texture = "Monophonic";
if (colorRange > 50) texture = "Polyphonic";
if (colorRange > 120) texture = "Harmonically Rich";
ctx.fillText(`Mood: ${mood}`, vizX, line1Y);
ctx.fillText(`Tempo: ${tempo}`, vizX, line2Y);
ctx.fillText(`Texture: ${texture}`, vizX, line3Y);
return canvas;
}
Apply Changes