You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(originalImg, numIdeas = 4, accentColor = "#8b5cf6") {
// Determine canvas dimensions
const sidePanelWidth = 500;
const padding = 40;
const maxImgWidth = 800;
const maxImgHeight = 800;
// Scale image to fit within max bounds
let scale = Math.min(maxImgWidth / originalImg.width, maxImgHeight / originalImg.height);
if (scale > 1) scale = 1; // Don't up-scale small images
const finalImgWidth = originalImg.width * scale;
const finalImgHeight = originalImg.height * scale;
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = finalImgWidth + sidePanelWidth;
canvas.height = Math.max(finalImgHeight, 750);
// 1. Draw base layout
// Background
ctx.fillStyle = '#0f172a'; // dark slate background
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Draw the original image on the left
// Give it a subtle background/border area
ctx.fillStyle = '#1e293b';
ctx.fillRect(0, 0, finalImgWidth, canvas.height);
// Center the image vertically if the canvas is taller
const imgYOffset = (canvas.height - finalImgHeight) / 2;
ctx.drawImage(originalImg, 0, imgYOffset, finalImgWidth, finalImgHeight);
// Draw Right Panel Background
ctx.fillStyle = '#0f172a';
ctx.fillRect(finalImgWidth, 0, sidePanelWidth, canvas.height);
// Function to draw multiline text
const drawWrappedText = (text, x, y, maxWidth, lineHeight, color, font) => {
ctx.fillStyle = color;
ctx.font = font;
const words = text.split(' ');
let line = '';
let currentY = y;
for (let n = 0; n < words.length; n++) {
let testLine = line + words[n] + ' ';
let metrics = ctx.measureText(testLine);
if (metrics.width > maxWidth && n > 0) {
ctx.fillText(line, x, currentY);
line = words[n] + ' ';
currentY += lineHeight;
} else {
line = testLine;
}
}
ctx.fillText(line, x, currentY);
return currentY;
};
// Helper to load external scripts iteratively
const loadScript = (src, globalObjName) => {
return new Promise((resolve, reject) => {
if (window[globalObjName]) {
return resolve(window[globalObjName]);
}
const script = document.createElement('script');
script.src = src;
script.onload = () => resolve(window[globalObjName]);
script.onerror = () => reject(new Error(`Failed to load ${src}`));
document.head.appendChild(script);
});
};
const uiX = finalImgWidth + padding;
let uiY = padding + 20;
// Output Loading State to Canvas temporarily
ctx.fillStyle = accentColor;
ctx.font = 'bold 24px system-ui, -apple-system, sans-serif';
ctx.fillText("✨ Generating AI Ideas...", uiX, uiY);
let predictions = [];
try {
// Load TensorFlow.js (core machine learning library)
await loadScript('https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@latest/dist/tf.min.js', 'tf');
// Load MobileNet (Image classification model)
await loadScript('https://cdn.jsdelivr.net/npm/@tensorflow-models/mobilenet@latest/dist/mobilenet.min.js', 'mobilenet');
const model = await window.mobilenet.load();
try {
predictions = await model.classify(originalImg);
} catch (classificationError) {
console.warn("AI Classification failed (likely CORS on canvas taint). Using structural fallback.");
predictions = [
{ className: 'mysterious artifact, concept art', probability: 0.95 },
{ className: 'geometric pattern, digital art', probability: 0.75 },
{ className: 'vibrant abstract entity', probability: 0.50 }
];
}
} catch (modelError) {
console.warn("Failed to load AI models. Falling back to default ideas.");
predictions = [{ className: 'creative visual element', probability: 0.99 }];
}
// Refresh Side Panel
ctx.fillStyle = '#0f172a';
ctx.fillRect(finalImgWidth, 0, sidePanelWidth, canvas.height);
uiY = padding + 30;
// Header section
ctx.fillStyle = accentColor;
ctx.font = 'bold 28px system-ui, -apple-system, sans-serif';
ctx.fillText("🤖 AI IDEA GENERATOR", uiX, uiY);
uiY += 50;
// Extracted Objects Section
ctx.fillStyle = '#e2e8f0'; // slate-200
ctx.font = 'bold 18px system-ui, -apple-system, sans-serif';
ctx.fillText("🔍 AI DETECTED SUBJECTS", uiX, uiY);
uiY += 30;
let mainSubject = "object";
const barMaxWidth = sidePanelWidth - (padding * 2) - 80;
if (predictions && predictions.length > 0) {
mainSubject = predictions[0].className.split(',')[0].trim();
predictions.forEach((p) => {
const probPercent = Math.round(p.probability * 100);
const label = p.className.split(',')[0].trim();
ctx.fillStyle = '#94a3b8'; // slate-400
ctx.font = '16px system-ui, -apple-system, sans-serif';
ctx.fillText(label.charAt(0).toUpperCase() + label.slice(1), uiX, uiY);
// Draw probability bar
ctx.fillStyle = '#1e293b';
ctx.fillRect(uiX, uiY + 10, barMaxWidth, 6);
ctx.fillStyle = accentColor;
ctx.fillRect(uiX, uiY + 10, barMaxWidth * p.probability, 6);
// Draw percentage
ctx.fillStyle = '#f8fafc';
ctx.fillText(`${probPercent}%`, uiX + barMaxWidth + 10, uiY + 16);
uiY += 45;
});
}
uiY += 20;
// Design & Prompt Ideas Section
ctx.fillStyle = '#e2e8f0';
ctx.font = 'bold 18px system-ui, -apple-system, sans-serif';
ctx.fillText(`💡 GENERATED PROMPT IDEAS`, uiX, uiY);
uiY += 30;
const baseStyles = [
"cyberpunk city setting, neon lighting, highly detailed unreal engine 5 render, trending on artstation",
"oil painting by Vincent Van Gogh, post-impressionism, vibrant swirling colors, thick impasto strokes",
"minimalist flat vector illustration, dribbble style design, clean lines, complementary pastel tones",
"steampunk mechanical aesthetic, intricate gears and brass components, moody dramatic lighting",
"dark atmospheric fantasy concept art, grimdark realism, volumetric fog, rim lighting",
"dreamy watercolor splash art, ethereal and light, translucent overlay, soft gradients",
"holographic 3D render, futuristic sci-fi design, glassmorphism, floating elements"
];
// Pick top N prompts randomly
const ideasToGenerate = Math.max(1, parseInt(numIdeas) || 4);
const selectedStyles = baseStyles.sort(() => 0.5 - Math.random()).slice(0, ideasToGenerate);
selectedStyles.forEach((style, index) => {
const promptText = `${index + 1}. A ${mainSubject} stylized as a ${style}.`;
uiY = drawWrappedText(promptText, uiX, uiY, sidePanelWidth - (padding * 2), 24, '#cbd5e1', 'italic 15px system-ui, -apple-system, sans-serif');
uiY += 25;
});
uiY += 10;
// Color Palette Section (A crucial "Idea/Moodboard" generation piece)
ctx.fillStyle = '#e2e8f0';
ctx.font = 'bold 18px system-ui, -apple-system, sans-serif';
ctx.fillText("🎨 HARMONIZED COLOR PALETTE", uiX, uiY);
uiY += 30;
// Fast Color Extraction using Canvas downsizing
let colorData;
const numColors = 5;
try {
const tinyCanvas = document.createElement('canvas');
tinyCanvas.width = numColors;
tinyCanvas.height = 1;
const tctx = tinyCanvas.getContext('2d');
// By drawing an image down to 5x1, we effectively get 5 regional average colors
tctx.drawImage(originalImg, 0, 0, numColors, 1);
colorData = tctx.getImageData(0, 0, numColors, 1).data;
} catch(err) {
// Fallback colors if Canvas gets tainted via CORS (cross-origin image restrictions)
colorData = new Uint8ClampedArray([
15, 23, 42, 255, // Dark Navy
139, 92, 246, 255, // Violet (Accent)
45, 212, 191, 255, // Teal
245, 158, 11, 255, // Amber
248, 250, 252, 255 // White Slate
]);
}
const boxSize = 50;
const boxGap = 15;
for (let i = 0; i < numColors; i++) {
const r = colorData[i * 4];
const g = colorData[i * 4 + 1];
const b = colorData[i * 4 + 2];
const hexColor = "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1).toUpperCase();
const boxX = uiX + (i * (boxSize + boxGap));
ctx.fillStyle = `rgb(${r},${g},${b})`;
ctx.beginPath();
ctx.roundRect(boxX, uiY, boxSize, boxSize, 8);
ctx.fill();
// Draw hex code underneath
ctx.fillStyle = '#64748b';
ctx.font = '12px system-ui, -apple-system, monospace';
ctx.fillText(hexColor, boxX, uiY + boxSize + 20);
}
// Footer Hint
ctx.fillStyle = '#475569';
ctx.font = '13px system-ui, -apple-system, sans-serif';
ctx.fillText("Drag, drop, and export these concepts to your moodboard tool.", uiX, canvas.height - 30);
return canvas;
}
Apply Changes