You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, siteTitle = "My Awesome Website", theme = "auto") {
// --- Helper: Color Analysis & Manipulation ---
/**
* Extracts a color palette from an image using color quantization.
* @param {HTMLImageElement} img The source image.
* @param {number} count The number of colors to return.
* @returns {Array<object>} An array of color objects like {r, g, b}.
*/
const getPalette = (img, count = 6) => {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d', { willReadFrequently: true });
// Downscale image for performance
const maxDim = 100;
const scale = Math.min(maxDim / img.width, maxDim / img.height, 1);
canvas.width = img.width * scale;
canvas.height = img.height * scale;
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height).data;
const colorCounts = {};
const colorSums = {};
// Quantize colors to 4 bits per channel (12-bit color space)
const bitShift = 4;
for (let i = 0; i < imageData.length; i += 4) {
const r = imageData[i];
const g = imageData[i + 1];
const b = imageData[i + 2];
const alpha = imageData[i + 3];
// Ignore transparent or near-transparent pixels
if (alpha < 128) {
continue;
}
// Create a quantized color key
const rKey = r >> bitShift;
const gKey = g >> bitShift;
const bKey = b >> bitShift;
const key = (rKey << 8) | (gKey << 4) | bKey;
if (!colorCounts[key]) {
colorCounts[key] = 0;
colorSums[key] = { r: 0, g: 0, b: 0 };
}
colorCounts[key]++;
colorSums[key].r += r;
colorSums[key].g += g;
colorSums[key].b += b;
}
const sortedKeys = Object.keys(colorCounts).sort((a, b) => colorCounts[b] - colorCounts[a]);
const palette = [];
for (let i = 0; i < Math.min(sortedKeys.length, count * 2); i++) {
const key = sortedKeys[i];
const numPixels = colorCounts[key];
const sum = colorSums[key];
const avgColor = {
r: Math.round(sum.r / numPixels),
g: Math.round(sum.g / numPixels),
b: Math.round(sum.b / numPixels),
};
palette.push(avgColor);
}
return palette.slice(0, count);
};
/**
* Calculates the relative luminance of an RGB color.
* @param {number} r Red value (0-255).
* @param {number} g Green value (0-255).
* @param {number} b Blue value (0-255).
* @returns {number} Luminance (0-1).
*/
const getLuminance = (r, g, b) => {
const a = [r, g, b].map(v => {
v /= 255;
return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
});
return a[0] * 0.2126 + a[1] * 0.7152 + a[2] * 0.0722;
};
/**
* Calculates the contrast ratio between two RGB colors.
* @param {object} rgb1 First color {r, g, b}.
* @param {object} rgb2 Second color {r, g, b}.
* @returns {number} The contrast ratio.
*/
const getContrast = (rgb1, rgb2) => {
const lum1 = getLuminance(rgb1.r, rgb1.g, rgb1.b);
const lum2 = getLuminance(rgb2.r, rgb2.g, rgb2.b);
const brightest = Math.max(lum1, lum2);
const darkest = Math.min(lum1, lum2);
return (brightest + 0.05) / (darkest + 0.05);
};
/**
* Converts an RGB color object to a CSS rgb() string.
*/
const rgbToCss = (rgb) => `rgb(${rgb.r}, ${rgb.g}, ${rgb.b})`;
/**
* Converts an RGB color to HSL.
*/
const 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 = 0, l = (max + min) / 2;
if (max !== min) {
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: h * 360, s: s * 100, l: l * 100 };
};
// --- Main "AI" Logic ---
// 1. Analyze Image to get color palette
const rawPalette = getPalette(originalImg, 10);
const paletteWithMeta = rawPalette.map(c => ({
...c,
luminance: getLuminance(c.r, c.g, c.b),
hsl: rgbToHsl(c.r, c.g, c.b)
}));
// 2. Select theme colors based on the palette
let themeToUse = theme;
if (theme === "auto") {
const averageLuminance = paletteWithMeta.reduce((acc, c) => acc + c.luminance, 0) / paletteWithMeta.length;
themeToUse = averageLuminance > 0.5 ? "light" : "dark";
}
paletteWithMeta.sort((a, b) => a.luminance - b.luminance);
let bgColor, textColor;
const darkFallback = {r: 18, g: 18, b: 18};
const lightFallback = {r: 250, g: 250, b: 250};
if (themeToUse === 'dark') {
bgColor = paletteWithMeta[0] || darkFallback;
textColor = paletteWithMeta[paletteWithMeta.length - 1] || lightFallback;
} else {
bgColor = paletteWithMeta[paletteWithMeta.length - 1] || lightFallback;
textColor = paletteWithMeta[0] || darkFallback;
}
// Ensure sufficient contrast for base text (WCAG AA)
if (getContrast(bgColor, textColor) < 4.5) {
textColor = getLuminance(bgColor.r, bgColor.g, bgColor.b) > 0.5 ? darkFallback : lightFallback;
}
// Pick primary and accent colors from the remaining palette
const remainingColors = paletteWithMeta.filter(c => c !== bgColor && c !== textColor);
// Sort by saturation to find vibrant colors
remainingColors.sort((a,b) => b.hsl.s - a.hsl.s);
let primaryColor = remainingColors[0] || textColor;
if (getContrast(bgColor, primaryColor) < 3) { // Ensure headings are readable (WCAG AA for large text)
primaryColor = textColor;
}
let accentColor = remainingColors[1] || primaryColor;
if (getContrast(bgColor, accentColor) < 3) {
accentColor = primaryColor;
}
// Choose the best text color (black or white) for the accent button
const accentContrastLight = getContrast(accentColor, {r:255,g:255,b:255});
const accentContrastDark = getContrast(accentColor, {r:0,g:0,b:0});
let accentTextColor = accentContrastLight > accentContrastDark ? lightFallback : darkFallback;
// 3. Import Google Font
const fontName = 'Montserrat';
const fontUrl = `https://fonts.googleapis.com/css2?family=${fontName.replace(' ', '+')}:wght@400;700&display=swap`;
if (!document.querySelector(`link[href="${fontUrl}"]`)) {
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = fontUrl;
document.head.appendChild(link);
}
// 4. Create CSS styles
const css = `
.ai-website, .ai-website * {
box-sizing: border-box;
}
.ai-website {
--bg-color: ${rgbToCss(bgColor)};
--text-color: ${rgbToCss(textColor)};
--primary-color: ${rgbToCss(primaryColor)};
--accent-color: ${rgbToCss(accentColor)};
--accent-text-color: ${rgbToCss(accentTextColor)};
--font-family: '${fontName}', sans-serif;
all: revert;
font-family: var(--font-family);
background-color: var(--bg-color);
color: var(--text-color);
text-align: center;
line-height: 1.6;
width: 100%;
overflow: hidden;
border: 1px solid rgba(128,128,128,0.2);
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
border-radius: 8px;
}
.ai-website h1, .ai-website h2, .ai-website h3 { margin: 0; padding: 0; line-height: 1.2; }
.ai-website p { margin: 0; }
.ai-website a { color: var(--primary-color); text-decoration: none; }
.ai-website-header { padding: 20px; display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid rgba(128,128,128,0.1); }
.ai-website-header h1 { font-size: 1.5em; color: var(--primary-color); }
.ai-website-nav a { color: var(--text-color); margin: 0 15px; opacity: 0.8; transition: opacity 0.3s ease; }
.ai-website-nav a:hover { opacity: 1; color: var(--primary-color); }
.ai-website-hero { padding: 60px 20px; background-image: linear-gradient(rgba(0,0,0,0.4), rgba(0,0,0,0.4)), url(${originalImg.src}); background-size: cover; background-position: center; color: white; text-shadow: 2px 2px 4px rgba(0,0,0,0.5); }
.ai-website-hero h2 { font-size: 2.5em; margin-bottom: 20px; color: white; }
.ai-website .btn { background-color: var(--accent-color); color: var(--accent-text-color) !important; padding: 12px 25px; border-radius: 5px; font-weight: bold; display: inline-block; margin-top: 20px; border: none; cursor: pointer; transition: transform 0.2s ease, box-shadow 0.2s ease; }
.ai-website .btn:hover { transform: translateY(-2px); box-shadow: 0 4px 10px rgba(0,0,0,0.2); }
.ai-website-section { padding: 50px 20px; max-width: 800px; margin: 0 auto; }
.ai-website-section h3 { font-size: 2em; color: var(--primary-color); margin-bottom: 20px; }
.ai-website-gallery { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px; padding: 20px; }
.ai-website-gallery img { width: 100%; height: 180px; object-fit: cover; border-radius: 5px; transition: transform 0.3s ease, filter 0.3s ease; }
.ai-website-gallery img:hover { transform: scale(1.05); filter: brightness(1.1); }
.ai-website-footer { padding: 20px; margin-top: 40px; border-top: 1px solid rgba(128,128,128,0.1); font-size: 0.9em; opacity: 0.7; }
`;
// 5. Create HTML Structure
const html = `
<header class="ai-website-header">
<h1>${siteTitle}</h1>
<nav class="ai-website-nav">
<a href="#">Home</a>
<a href="#">About</a>
<a href="#">Contact</a>
</nav>
</header>
<main>
<section class="ai-website-hero">
<h2>Crafted by AI, Inspired by Your Vision</h2>
<p>A seamless digital experience, generated in seconds from your image.</p>
<a href="#" class="btn">Learn More</a>
</section>
<section class="ai-website-section">
<h3>Discover Our Story</h3>
<p>Our intelligent design system analyzed your image to create a unique and compelling visual narrative. By extracting key colors, themes, and moods, we've built a foundation for a website that truly represents your brand. This is a journey into digital creativity.</p>
</section>
<section class="ai-website-section">
<h3>Gallery</h3>
<div class="ai-website-gallery">
<img src="${originalImg.src}" alt="Gallery image 1" style="filter: saturate(1.2);">
<img src="${originalImg.src}" alt="Gallery image 2" style="filter: grayscale(1);">
<img src="${originalImg.src}" alt="Gallery image 3" style="filter: sepia(0.8);">
<img src="${originalImg.src}" alt="Gallery image 4" style="filter: hue-rotate(90deg);">
</div>
</section>
</main>
<footer class="ai-website-footer">
<p>© ${new Date().getFullYear()} ${siteTitle}. Powered by AI.</p>
</footer>
`;
// 6. Combine and Return the final element
const container = document.createElement('div');
container.className = 'ai-website';
// Using a shadow DOM would be ideal for encapsulation, but for simple display,
// injecting a scoped style tag is sufficient and works everywhere.
const styleElement = document.createElement('style');
styleElement.textContent = css;
const contentWrapper = document.createElement('div');
contentWrapper.innerHTML = html;
container.appendChild(styleElement);
container.appendChild(contentWrapper);
return container;
}
Apply Changes