Please bookmark this page to avoid losing your image tool!

Unknown Cartoon Character Identifier

(Free & Supports Bulk Upload)

Drag & drop your images here or

The result will appear here...
You can edit the below JavaScript code to customize the image tool.
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;
}

Free Image Tool Creator

Can't find the image tool you're looking for?
Create one based on your own needs now!

Description

The Unknown Cartoon Character Identifier is an image processing tool designed to help users identify animated characters from images. The tool allows users to upload an image and manually crop or select specific areas, such as a character’s face, to improve recognition accuracy. Once a selection is made, users can search for the character using integrated search options including Google Lens, Yandex Images, or a specialized anime database (Trace.moe) that can provide specific details like the anime title, episode number, and even video clips. This tool is ideal for fans looking to identify characters from obscure cartoons, anime, or screenshots they encounter online.

Leave a Reply

Your email address will not be published. Required fields are marked *