You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(originalImg, language = "ru") {
// Styling constants
const uiBg = '#1e1e1e';
const uiText = '#ffffff';
const panelBg = '#2a2a2a';
const accentColor = '#61dafb';
// Main container setup
const container = document.createElement('div');
container.style.width = '100%';
container.style.maxWidth = '800px';
container.style.margin = '0 auto';
container.style.fontFamily = 'system-ui, -apple-system, sans-serif';
container.style.backgroundColor = uiBg;
container.style.color = uiText;
container.style.padding = '25px';
container.style.borderRadius = '12px';
container.style.boxShadow = '0 10px 20px rgba(0,0,0,0.4)';
container.style.boxSizing = 'border-box';
// Title
const title = document.createElement('h2');
title.innerText = language === 'ru' ? 'π ΠΠΎΠΈΡΠΊ ΠΡΠ»ΡΡΡΡΠ½ΠΎΠ³ΠΎ ΠΠ΅ΡΡΠΎΠ½Π°ΠΆΠ°' : 'π Cartoon Character Identifier';
title.style.margin = '0 0 10px 0';
title.style.textAlign = 'center';
container.appendChild(title);
// Instructions
const instructions = document.createElement('p');
instructions.innerText = language === 'ru'
? 'Π‘ΠΈΡΡΠ΅ΠΌΡ ΡΠ°ΡΠΏΠΎΠ·Π½Π°Π²Π°Π½ΠΈΡ ΡΠ°Π±ΠΎΡΠ°ΡΡ Π»ΡΡΡΠ΅, Π΅ΡΠ»ΠΈ Π²ΡΡΠ΅Π·Π°ΡΡ Π»ΠΈΡΠΎ. ΠΡΠ΄Π΅Π»ΠΈΡΠ΅ ΠΏΠ΅ΡΡΠΎΠ½Π°ΠΆΠ° ΠΌΡΡΠΊΠΎΠΉ Π½Π° ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΠΈ Π½ΠΈΠΆΠ΅, Π·Π°ΡΠ΅ΠΌ Π·Π°ΠΏΡΡΡΠΈΡΠ΅ ΠΏΠΎΠΈΡΠΊ.'
: 'Recognition works best on faces. Draw a box around the character\'s face below to crop, then use a search engine.';
instructions.style.fontSize = '14px';
instructions.style.color = '#bbb';
instructions.style.textAlign = 'center';
instructions.style.marginBottom = '20px';
container.appendChild(instructions);
// Canvas container (relative for absolute positioned elements)
const canvasWrap = document.createElement('div');
canvasWrap.style.display = 'flex';
canvasWrap.style.justifyContent = 'center';
canvasWrap.style.position = 'relative';
canvasWrap.style.margin = '0 auto';
canvasWrap.style.overflow = 'hidden';
// Canvas configuration
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const MAX_WIDTH = 600;
const MAX_HEIGHT = 500;
let canvasScale = 1;
if (originalImg.width > MAX_WIDTH || originalImg.height > MAX_HEIGHT) {
canvasScale = Math.min(MAX_WIDTH / originalImg.width, MAX_HEIGHT / originalImg.height);
}
canvas.width = originalImg.width * canvasScale;
canvas.height = originalImg.height * canvasScale;
canvas.style.maxWidth = '100%';
canvas.style.height = 'auto'; // Ensures mobile responsiveness
canvas.style.cursor = 'crosshair';
canvas.style.boxShadow = '0 4px 12px rgba(0,0,0,0.5)';
canvas.style.borderRadius = '6px';
// Initial draw
ctx.drawImage(originalImg, 0, 0, canvas.width, canvas.height);
canvasWrap.appendChild(canvas);
container.appendChild(canvasWrap);
// Crop functionality states
let isDrawing = false;
let startX = 0, startY = 0;
let rect = { x: 0, y: 0, w: canvas.width, h: canvas.height };
let hasCustomCrop = false;
// Core draw update
function redrawImage() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(originalImg, 0, 0, canvas.width, canvas.height);
if (hasCustomCrop || isDrawing) {
// Draw dark overlay
ctx.fillStyle = 'rgba(0, 0, 0, 0.6)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Clear cropped area
ctx.clearRect(rect.x, rect.y, rect.w, rect.h);
// Draw cropped area
ctx.drawImage(originalImg,
rect.x / canvasScale, rect.y / canvasScale, rect.w / canvasScale, rect.h / canvasScale,
rect.x, rect.y, rect.w, rect.h
);
// Draw border
ctx.strokeStyle = accentColor;
ctx.lineWidth = 2;
ctx.strokeRect(rect.x, rect.y, rect.w, rect.h);
}
}
canvas.addEventListener('mousedown', (e) => {
const bcr = canvas.getBoundingClientRect();
const scaleX = canvas.width / bcr.width;
const scaleY = canvas.height / bcr.height;
startX = (e.clientX - bcr.left) * scaleX;
startY = (e.clientY - bcr.top) * scaleY;
isDrawing = true;
hasCustomCrop = true;
rect = { x: startX, y: startY, w: 0, h: 0 };
});
canvas.addEventListener('mousemove', (e) => {
if (!isDrawing) return;
const bcr = canvas.getBoundingClientRect();
const scaleX = canvas.width / bcr.width;
const scaleY = canvas.height / bcr.height;
const currentX = (e.clientX - bcr.left) * scaleX;
const currentY = (e.clientY - bcr.top) * scaleY;
rect.w = currentX - startX;
rect.h = currentY - startY;
redrawImage();
});
canvas.addEventListener('mouseup', () => {
isDrawing = false;
// Normalize negative width/height (when selecting bottom-up or right-to-left)
if (rect.w < 0) { rect.x += rect.w; rect.w = Math.abs(rect.w); }
if (rect.h < 0) { rect.y += rect.h; rect.h = Math.abs(rect.h); }
// Prevent accidental micro-clicks by resetting small boxes
if (rect.w <= 15 || rect.h <= 15) {
hasCustomCrop = false;
rect = { x: 0, y: 0, w: canvas.width, h: canvas.height };
}
redrawImage();
if (hasCustomCrop) resetCropBtn.style.display = 'block';
});
canvas.addEventListener('mouseleave', () => {
if (isDrawing) {
isDrawing = false;
redrawImage();
}
});
// Reset Crop Button
const resetCropBtn = document.createElement('button');
resetCropBtn.innerText = language === 'ru' ? 'Π‘Π±ΡΠΎΡΠΈΡΡ Π²ΡΠ΄Π΅Π»Π΅Π½ΠΈΠ΅' : 'Reset Crop';
resetCropBtn.style.position = 'absolute';
resetCropBtn.style.top = '10px';
resetCropBtn.style.right = '10px';
resetCropBtn.style.padding = '6px 12px';
resetCropBtn.style.fontSize = '12px';
resetCropBtn.style.cursor = 'pointer';
resetCropBtn.style.backgroundColor = 'rgba(0,0,0,0.7)';
resetCropBtn.style.color = '#fff';
resetCropBtn.style.border = '1px solid #fff';
resetCropBtn.style.borderRadius = '4px';
resetCropBtn.style.display = 'none';
resetCropBtn.onclick = () => {
hasCustomCrop = false;
rect = { x: 0, y: 0, w: canvas.width, h: canvas.height };
resetCropBtn.style.display = 'none';
redrawImage();
};
canvasWrap.appendChild(resetCropBtn);
// AI Scanner Visual Effect
let scanPos = 0;
let scanDir = 1;
let scanning = true;
function runScanner() {
if (!scanning) {
redrawImage();
return;
}
redrawImage();
ctx.beginPath();
ctx.fillStyle = 'rgba(97, 218, 251, 0.4)';
ctx.fillRect(0, scanPos - 5, canvas.width, 10);
ctx.fillStyle = '#61dafb';
ctx.fillRect(0, scanPos, canvas.width, 2);
ctx.closePath();
scanPos += 4 * scanDir;
if (scanPos >= canvas.height) scanDir = -1;
if (scanPos <= 0) scanDir = 1;
requestAnimationFrame(runScanner);
}
runScanner();
setTimeout(() => { scanning = false; }, 2500);
// Status / Result Display
const statusBox = document.createElement('div');
statusBox.style.padding = '15px';
statusBox.style.marginTop = '20px';
statusBox.style.backgroundColor = panelBg;
statusBox.style.borderRadius = '8px';
statusBox.style.minHeight = '50px';
statusBox.style.textAlign = 'center';
statusBox.style.border = '1px solid #333';
statusBox.innerText = language === 'ru' ? 'ΠΠΆΠΈΠ΄Π°Π½ΠΈΠ΅ Π΄Π΅ΠΉΡΡΠ²ΠΈΠΉ...' : 'Awaiting action...';
container.appendChild(statusBox);
// Action Area container
const btnGroup = document.createElement('div');
btnGroup.style.display = 'flex';
btnGroup.style.flexWrap = 'wrap';
btnGroup.style.justifyContent = 'center';
btnGroup.style.gap = '15px';
btnGroup.style.marginTop = '20px';
container.appendChild(btnGroup);
// Image processor for export
function getCroppedBlob() {
return new Promise((resolve) => {
const tempCanvas = document.createElement('canvas');
// Upscale the cropped dimensions proportionally to natively high quality resolution
tempCanvas.width = rect.w / canvasScale;
tempCanvas.height = rect.h / canvasScale;
const tCtx = tempCanvas.getContext('2d');
tCtx.drawImage(originalImg,
rect.x / canvasScale, rect.y / canvasScale, rect.w / canvasScale, rect.h / canvasScale,
0, 0, tempCanvas.width, tempCanvas.height
);
tempCanvas.toBlob(resolve, 'image/png');
});
}
// Programmatic form submit logic bypasses async popup blockers by reusing pre-opened tabs
function submitToSearchEngine(url, parameterName, blob, targetTabName) {
const file = new File([blob], 'avatar.png', { type: 'image/png' });
// Use DataTransfer to populate form input natively
const dt = new DataTransfer();
dt.items.add(file);
const form = document.createElement('form');
form.method = 'POST';
form.action = url;
form.enctype = 'multipart/form-data';
form.target = targetTabName;
const fileInput = document.createElement('input');
fileInput.type = 'file';
fileInput.name = parameterName;
fileInput.files = dt.files;
form.appendChild(fileInput);
document.body.appendChild(form);
form.submit();
setTimeout(() => document.body.removeChild(form), 1000);
}
function createBtn(text, hexColor, onClick) {
const btn = document.createElement('button');
btn.innerText = text;
btn.style.padding = '12px 18px';
btn.style.fontSize = '14px';
btn.style.fontWeight = '600';
btn.style.cursor = 'pointer';
btn.style.backgroundColor = hexColor;
btn.style.color = '#fff';
btn.style.border = 'none';
btn.style.borderRadius = '6px';
btn.style.transition = 'opacity 0.2s, transform 0.1s';
btn.onmouseover = () => btn.style.opacity = '0.85';
btn.onmouseout = () => btn.style.opacity = '1';
btn.onmousedown = () => btn.style.transform = 'scale(0.97)';
btn.onmouseup = () => btn.style.transform = 'scale(1)';
btn.onclick = onClick;
return btn;
}
// Google Lens Identifier
const lensBtn = createBtn('Google Lens', '#4285F4', () => {
const targetName = 'lens_target_' + Date.now();
window.open('about:blank', targetName); // Ensure popup triggers syncronously
statusBox.innerHTML = language === 'ru' ? 'ΠΠ±ΡΠ°Π±ΠΎΡΠΊΠ° Π΄Π°Π½Π½ΡΡ
... ΠΡΠΊΡΡΠ²Π°Π΅ΠΌ Google Lens.' : 'Processing image... Opening Google Lens.';
getCroppedBlob().then(blob => {
submitToSearchEngine('https://lens.google.com/upload', 'encoded_image', blob, targetName);
statusBox.innerHTML = language === 'ru' ? 'β
Π Π΅Π·ΡΠ»ΡΡΠ°Ρ ΠΎΡΠΊΡΡΡ Π² Π½ΠΎΠ²ΠΎΠΉ Π²ΠΊΠ»Π°Π΄ΠΊΠ΅ (Google)' : 'β
Launched search in new tab (Google).';
});
});
// Yandex Images (Top Tier for obscure Cartoons)
const yandexBtn = createBtn(language === 'ru' ? 'Π―Π½Π΄Π΅ΠΊΡ ΠΠ°ΡΡΠΈΠ½ΠΊΠΈ (ΠΡΡΡΠ΅ Π΄Π»Ρ ΠΌΡΠ»ΡΡΡΠΈΠ»ΡΠΌΠΎΠ²)' : 'Yandex (Best for Cartoons)', '#fc3f1d', () => {
const targetName = 'yandex_target_' + Date.now();
window.open('about:blank', targetName);
statusBox.innerHTML = language === 'ru' ? 'ΠΠ°Π³ΡΡΠ·ΠΊΠ° Π² Π―Π½Π΄Π΅ΠΊΡ.ΠΠ°ΡΡΠΈΠ½ΠΊΠΈ...' : 'Uploading snapshot to Yandex...';
getCroppedBlob().then(blob => {
submitToSearchEngine('https://yandex.com/images/search?rpt=imageview', 'upfile', blob, targetName);
statusBox.innerHTML = language === 'ru' ? 'β
Π Π΅Π·ΡΠ»ΡΡΠ°Ρ ΠΎΡΠΊΡΡΡ Π² Π½ΠΎΠ²ΠΎΠΉ Π²ΠΊΠ»Π°Π΄ΠΊΠ΅ (Π―Π½Π΄Π΅ΠΊΡ)' : 'β
Launched search in new tab (Yandex).';
});
});
// Trace.moe (Built-in Anime Scanner API)
const animeBtn = createBtn('Trace.moe (ΠΠ½ΠΈΠΌΠ΅ ΡΠΊΠ°Π½Π΅Ρ)', '#9b59b6', async () => {
statusBox.innerHTML = language === 'ru' ? 'ΠΠΎΠΈΡΠΊ ΠΏΠ΅ΡΡΠΎΠ½Π°ΠΆΠ° ΠΏΠΎ Π±Π°Π·Π΅ ΠΠ½ΠΈΠΌΠ΅...' : 'Scanning via Anime Database (Trace.moe)...';
const blob = await getCroppedBlob();
try {
const formData = new FormData();
formData.append('image', blob);
const res = await fetch('https://api.trace.moe/search', {
method: 'POST',
body: formData
});
const data = await res.json();
if (data.error) throw new Error(data.error);
if (data.result && data.result.length > 0) {
const bestMatch = data.result[0];
const similarity = (bestMatch.similarity * 100).toFixed(1);
statusBox.innerHTML = `
<div style="text-align:left; max-width: 400px; margin: 0 auto; line-height: 1.4;">
<strong style="color: #61dafb;">${language === 'ru' ? 'ΠΠ°ΠΉΠ΄Π΅Π½ΠΎ ΡΠΎΠ²ΠΏΠ°Π΄Π΅Π½ΠΈΠ΅!' : 'Match Found!'}</strong><br>
<b>${language === 'ru' ? 'ΠΠ½ΠΈΠΌΠ΅' : 'Anime'}:</b> ${bestMatch.filename}<br>
<b>${language === 'ru' ? 'ΠΠΏΠΈΠ·ΠΎΠ΄' : 'Episode'}:</b> ${bestMatch.episode || 'N/A'}<br>
<b>${language === 'ru' ? 'Π’ΠΎΡΠ½ΠΎΡΡΡ' : 'Confidence'}:</b> ${similarity}% <br>
</div>
`;
// Add preview video element if it's available
if (bestMatch.video) {
const vid = document.createElement('video');
vid.src = bestMatch.video;
vid.autoplay = true;
vid.loop = true;
vid.muted = true;
vid.style.maxWidth = '100%';
vid.style.maxHeight = '200px';
vid.style.borderRadius = '8px';
vid.style.marginTop = '15px';
vid.style.boxShadow = '0 4px 8px rgba(0,0,0,0.4)';
statusBox.appendChild(vid);
}
} else {
statusBox.innerHTML = language === 'ru' ? 'β Π Π±Π°Π·Π΅ Π°Π½ΠΈΠΌΠ΅ ΡΠΎΠ²ΠΏΠ°Π΄Π΅Π½ΠΈΠΉ Π½Π΅ Π½Π°ΠΉΠ΄Π΅Π½ΠΎ!' : 'β No anime matches found.';
}
} catch (e) {
statusBox.innerHTML = language === 'ru' ? 'β ΠΡΠΈΠ±ΠΊΠ° ΡΠ΅ΡΠΈ ΠΏΡΠΈ ΠΏΠΎΠΈΡΠΊΠ΅ Π°Π½ΠΈΠΌΠ΅.' : 'β Network Error contacting Anime DB.';
}
});
btnGroup.appendChild(lensBtn);
btnGroup.appendChild(yandexBtn);
btnGroup.appendChild(animeBtn);
return container;
}
Apply Changes