You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, languageFilter = "Both") {
// Determine language preferences
const filter = languageFilter.toLowerCase();
const showJP = filter === "both" || filter === "japanese" || filter === "jp";
const showEN = filter === "both" || filter === "english" || filter === "en";
// 1. Image Analysis (Extract colors to determine character "vibe")
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
// Resize down for fast processing
const sampleSize = 64;
canvas.width = sampleSize;
canvas.height = sampleSize;
ctx.drawImage(originalImg, 0, 0, sampleSize, sampleSize);
const imageData = ctx.getImageData(0, 0, sampleSize, sampleSize).data;
let rSum = 0, gSum = 0, bSum = 0;
let pixelCount = 0;
for (let i = 0; i < imageData.length; i += 4) {
// Skip transparent pixels
if (imageData[i + 3] < 128) continue;
rSum += imageData[i];
gSum += imageData[i + 1];
bSum += imageData[i + 2];
pixelCount++;
}
if (pixelCount === 0) pixelCount = 1; // Prevent division by zero
const avgR = Math.round(rSum / pixelCount);
const avgG = Math.round(gSum / pixelCount);
const avgB = Math.round(bSum / pixelCount);
// Convert RGB to HSL
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, s, l = (max + min) / 2;
if (max === min) {
h = s = 0;
} else {
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];
}
const [h, s, l] = rgbToHsl(avgR, avgG, avgB);
// Deterministic seed based on image properties to pick specific VAs from array randomly
const magicSeed = Math.floor(avgR + avgG + avgB + originalImg.width + originalImg.height);
// 2. Character Archetype & Voice Actor Database
const db = [
{
archetype: "The Brooding Mastermind / Edgy Rival",
vibe: "Cool, Dark, and Intense",
reason: "The cool and dark tones in this image suggest a character with a mysterious, calculating, or intense personality.",
jp: ["Mamoru Miyano", "Hiroshi Kamiya", "Kenjiro Tsuda", "Takehito Koyasu", "Yuichi Nakamura", "Tatsuhisa Suzuki"],
en: ["Matthew Mercer", "Steve Blum", "Johnny Yong Bosch", "Crispin Freeman", "David Lodge", "Ray Chase"],
match: (h, s, l) => (l < 40 || (h > 200 && h < 280))
},
{
archetype: "The Hot-Blooded Protagonist",
vibe: "Warm, Vibrant, and Energetic",
reason: "High saturation and warm colors radiate pure shonen energy, indicating a passionate character who never gives up.",
jp: ["Yuki Kaji", "Junko Takeuchi", "Natsuki Hanae", "Daiki Yamashita", "Nobuhiko Okamoto", "Yoshitsugu Matsuoka"],
en: ["Bryce Papenbrook", "Yuri Lowenthal", "Justin Briner", "Max Mittelman", "Kyle McCarley", "Todd Haberkorn"],
match: (h, s, l) => (s > 45 && (h < 50 || h > 330))
},
{
archetype: "The Cheerful Optimist / Magical Girl",
vibe: "Bright, Colorful, and Uplifting",
reason: "Bright and pastel visual signatures suggest a bubbly, optimistic, or magical personality.",
jp: ["Kana Hanazawa", "Rie Takahashi", "Aoi Yuki", "Ayane Sakura", "Inori Minase", "Maaya Uchida"],
en: ["Laura Bailey", "Cherami Leigh", "Christine Marie Cabanos", "Erica Mendez", "Sarah Anne Williams", "Kira Buckland"],
match: (h, s, l) => (l > 55 && s > 40 && (h > 280 || h < 60))
},
{
archetype: "The Wise Mentor / Calm Presence",
vibe: "Earthy, Muted, and Balanced",
reason: "The muted/earth tones reflect a grounded character who offers guidance, tranquility, or healing.",
jp: ["Shinichiro Miki", "Takahiro Sakurai", "Yoshimasa Hosoya", "Saori Hayami", "Maaya Sakamoto"],
en: ["Liam O'Brien", "J. Michael Tatum", "Jamieson Price", "Erica Lindbeck", "Allegra Clark"],
match: (h, s, l) => (s <= 40 || (h > 60 && h < 160))
},
{
archetype: "The Stoic Defender / Heavy Hitter",
vibe: "Solid, Heavy, and Unwavering",
reason: "Deep, solid colors with lower saturation point to a tank-like or stoic veteran character.",
jp: ["Akio Otsuka", "Tomokazu Sugita", "Kenta Miyake", "Romi Park", "Fumihiko Tachiki"],
en: ["Patrick Seitz", "Christopher Sabat", "Keith David", "Mary Elizabeth McGlynn", "Richard Epcar"],
match: (h, s, l) => (l <= 50 && s < 45)
}
];
const fallback = {
archetype: "The Enigmatic Wildcard",
vibe: "Complex and Unpredictable",
reason: "The unique mix of tones doesn't fit standard molds, hinting at a highly complex or eccentric character.",
jp: ["Akira Ishida", "Miyuki Sawashiro", "Takehito Koyasu", "Tomokazu Sugita"],
en: ["Todd Haberkorn", "Tara Strong", "Robbie Daymond", "Erica Lindbeck"]
};
// Find first matching archetype
let selectedArchetype = fallback;
for (const entry of db) {
if (entry.match(h, s, l)) {
selectedArchetype = entry;
break;
}
}
// Select specific actors deterministically based on image properties
const jpActor = selectedArchetype.jp[magicSeed % selectedArchetype.jp.length];
const enActor = selectedArchetype.en[magicSeed % selectedArchetype.en.length];
// Get hex color for UI flavor
const hexColor = `#${Math.max(0, avgR).toString(16).padStart(2,'0')}${Math.max(0, avgG).toString(16).padStart(2,'0')}${Math.max(0, avgB).toString(16).padStart(2,'0')}`;
// 3. Build Output HTML Element
const container = document.createElement('div');
container.style.fontFamily = "'Segoe UI', Roboto, Helvetica, Arial, sans-serif";
container.style.backgroundColor = "#1e1e24";
container.style.color = "#ffffff";
container.style.padding = "25px";
container.style.borderRadius = "16px";
container.style.boxShadow = "0 10px 30px rgba(0,0,0,0.5)";
container.style.maxWidth = "500px";
container.style.margin = "0 auto";
container.style.lineHeight = "1.5";
container.style.borderTop = `6px solid ${hexColor}`;
// Draw original image nicely
const previewCanvas = document.createElement('canvas');
const pCtx = previewCanvas.getContext('2d');
const pSize = 150;
previewCanvas.width = pSize;
previewCanvas.height = pSize;
// Cover drawing logic for square aspect ratio
const imgRatio = originalImg.width / originalImg.height;
let sWidth = originalImg.width, sHeight = originalImg.height;
let sx = 0, sy = 0;
if (imgRatio > 1) { // Landscape
sWidth = originalImg.height;
sx = (originalImg.width - originalImg.height) / 2;
} else { // Portrait
sHeight = originalImg.width;
sy = (originalImg.height - originalImg.width) / 2;
}
pCtx.beginPath();
pCtx.arc(pSize/2, pSize/2, pSize/2, 0, Math.PI * 2);
pCtx.clip();
pCtx.drawImage(originalImg, sx, sy, sWidth, sHeight, 0, 0, pSize, pSize);
previewCanvas.style.display = "block";
previewCanvas.style.margin = "0 auto 20px auto";
previewCanvas.style.border = `4px solid ${hexColor}`;
previewCanvas.style.borderRadius = "50%";
previewCanvas.style.boxShadow = "0 4px 10px rgba(0,0,0,0.4)";
// Constructing innner HTML structurally
const header = document.createElement('h2');
header.style.textAlign = "center";
header.style.marginTop = "0";
header.style.marginBottom = "25px";
header.style.fontSize = "22px";
header.style.letterSpacing = "0.5px";
header.innerText = "🎤 Voice Actor Idea Generator";
const archetypeBadge = document.createElement('div');
archetypeBadge.style.backgroundColor = 'rgba(255,255,255,0.1)';
archetypeBadge.style.padding = '15px';
archetypeBadge.style.borderRadius = '8px';
archetypeBadge.style.marginBottom = '20px';
archetypeBadge.style.textAlign = 'center';
archetypeBadge.innerHTML = `
<span style="display:block; font-size: 13px; text-transform: uppercase; letter-spacing: 1px; color: #aaa; margin-bottom: 5px;">Dominant Vibe</span>
<strong style="font-size: 18px; color: ${hexColor}; filter: brightness(1.5)">${selectedArchetype.archetype}</strong>
<p style="font-size: 14px; margin: 10px 0 0 0; color: #ccc; font-style: italic;">"${selectedArchetype.reason}"</p>
`;
const vaContainer = document.createElement('div');
vaContainer.style.display = "flex";
vaContainer.style.flexDirection = "column";
vaContainer.style.gap = "15px";
function createVaCard(region, actorName) {
const card = document.createElement('div');
card.style.background = "linear-gradient(135deg, rgba(255,255,255,0.05) 0%, rgba(255,255,255,0.01) 100%)";
card.style.border = "1px solid rgba(255,255,255,0.1)";
card.style.borderRadius = "8px";
card.style.padding = "15px";
card.style.display = "flex";
card.style.alignItems = "center";
card.style.justifyContent = "space-between";
card.innerHTML = `
<div style="font-size: 13px; font-weight: bold; color: #888; text-transform: uppercase;">${region} Cast</div>
<div style="font-size: 18px; font-weight: 600;">${actorName}</div>
`;
return card;
}
if (showJP) vaContainer.appendChild(createVaCard("Japanese", jpActor));
if (showEN) vaContainer.appendChild(createVaCard("English", enActor));
// Append all elements to container
container.appendChild(header);
container.appendChild(previewCanvas);
container.appendChild(archetypeBadge);
container.appendChild(vaContainer);
return container;
}
Apply Changes