You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(originalImg, titleText = "Image to Movie Search") {
// 1. Create Main Container
const container = document.createElement("div");
container.style.fontFamily = "system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif";
container.style.maxWidth = "600px";
container.style.margin = "0 auto";
container.style.background = "#ffffff";
container.style.border = "1px solid #e0e0e0";
container.style.borderRadius = "12px";
container.style.boxShadow = "0 4px 12px rgba(0,0,0,0.1)";
container.style.overflow = "hidden";
// 2. Header
const header = document.createElement("div");
header.style.background = "#1a1a2e";
header.style.color = "#ffffff";
header.style.padding = "20px";
header.style.textAlign = "center";
const title = document.createElement("h2");
title.innerText = "🎬 " + titleText;
title.style.margin = "0";
title.style.fontSize = "24px";
title.style.fontWeight = "600";
const subtitle = document.createElement("p");
subtitle.innerText = "Identify anime screenshots or extract text from movie posters";
subtitle.style.margin = "8px 0 0 0";
subtitle.style.fontSize = "14px";
subtitle.style.color = "#a0a0b0";
header.appendChild(title);
header.appendChild(subtitle);
container.appendChild(header);
// 3. Image Preview Area
const previewBlock = document.createElement("div");
previewBlock.style.padding = "20px";
previewBlock.style.textAlign = "center";
previewBlock.style.background = "#f4f5f7";
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
// Scale large images to a reasonable maximum width to speed up OCR and API loads
const maxW = 560;
let scale = 1;
if (originalImg.width > maxW) {
scale = maxW / originalImg.width;
}
canvas.width = originalImg.width * scale;
canvas.height = originalImg.height * scale;
ctx.drawImage(originalImg, 0, 0, canvas.width, canvas.height);
canvas.style.maxWidth = "100%";
canvas.style.boxShadow = "0 2px 8px rgba(0,0,0,0.2)";
canvas.style.borderRadius = "8px";
previewBlock.appendChild(canvas);
container.appendChild(previewBlock);
// 4. Action Buttons Layer
const actionsBlock = document.createElement("div");
actionsBlock.style.padding = "0 20px 20px 20px";
actionsBlock.style.display = "flex";
actionsBlock.style.gap = "10px";
actionsBlock.style.justifyContent = "center";
const btnAnime = document.createElement("button");
btnAnime.innerText = "🔍 Search Anime Scene";
Object.assign(btnAnime.style, {
flex: "1",
padding: "12px",
background: "#e50914",
color: "white",
border: "none",
borderRadius: "6px",
fontSize: "14px",
fontWeight: "bold",
cursor: "pointer",
transition: "opacity 0.2s"
});
const btnPoster = document.createElement("button");
btnPoster.innerText = "📝 Extract Poster Text";
Object.assign(btnPoster.style, {
flex: "1",
padding: "12px",
background: "#f5c518",
color: "#000",
border: "none",
borderRadius: "6px",
fontSize: "14px",
fontWeight: "bold",
cursor: "pointer",
transition: "opacity 0.2s"
});
// Hover effects
btnAnime.onmouseenter = () => btnAnime.style.opacity = "0.85";
btnAnime.onmouseleave = () => btnAnime.style.opacity = "1";
btnPoster.onmouseenter = () => btnPoster.style.opacity = "0.85";
btnPoster.onmouseleave = () => btnPoster.style.opacity = "1";
actionsBlock.appendChild(btnAnime);
actionsBlock.appendChild(btnPoster);
container.appendChild(actionsBlock);
// 5. Results Layer
const resultsBlock = document.createElement("div");
resultsBlock.style.padding = "20px";
resultsBlock.style.minHeight = "80px";
resultsBlock.style.borderTop = "1px solid #e0e0e0";
resultsBlock.style.background = "#fff";
resultsBlock.innerHTML = `<p style="text-align:center; color:#888; font-style:italic; margin:0;">Results will appear here...</p>`;
container.appendChild(resultsBlock);
// Helper functions
const getBlob = () => new Promise(res => canvas.toBlob(res, "image/jpeg", 0.9));
function setButtonsDisabled(state) {
btnAnime.disabled = state;
btnPoster.disabled = state;
btnAnime.style.cursor = state ? 'not-allowed' : 'pointer';
btnPoster.style.cursor = state ? 'not-allowed' : 'pointer';
btnAnime.style.opacity = state ? "0.6" : "1";
btnPoster.style.opacity = state ? "0.6" : "1";
}
// --- Search Feature 1: Trace.moe Engine (Anime Scene Reverse Image Search) ---
btnAnime.onclick = async () => {
setButtonsDisabled(true);
resultsBlock.innerHTML = `<div style="text-align:center; color:#666;">
<p>⌛ Uploading and searching anime database (trace.moe)...</p>
</div>`;
try {
const blob = await getBlob();
const formData = new FormData();
formData.append("image", blob);
const response = await fetch("https://api.trace.moe/search", {
method: "POST",
body: formData
});
if (!response.ok) throw new Error("API responded with an error");
const data = await response.json();
if (data.result && data.result.length > 0) {
const bestMatch = data.result[0];
const accuracy = (bestMatch.similarity * 100).toFixed(1);
const isAccurate = bestMatch.similarity > 0.85;
// Clean filename to look more like a standard title
const titleMatch = bestMatch.filename.replace(/\.[^/.]+$/, "").replace(/\[.*?\]/g, '').replace(/_|-/g, ' ').trim();
resultsBlock.innerHTML = `
<h4 style="margin:0 0 12px 0; color:#1a1a2e; font-size: 16px;">Anime Match Found!</h4>
<div style="background:#f4f5f7; padding:15px; border-radius:8px; display:flex; flex-direction:column; gap:8px; font-size:14px;">
<div><strong style="color:#333;">Title:</strong> ${titleMatch}</div>
<div><strong style="color:#333;">Episode:</strong> ${bestMatch.episode || "Movie/OVA (N/A)"}</div>
<div>
<strong style="color:#333;">Confidence:</strong>
<span style="color:${isAccurate ? '#28a745' : '#e50914'}; font-weight:bold;">${accuracy}%</span>
</div>
${bestMatch.video ? `<video src="${bestMatch.video}" controls autoplay loop style="width:100%; border-radius:6px; margin-top:10px; max-height:220px; object-fit:contain; background:#000;"></video>` : ''}
</div>
`;
} else {
resultsBlock.innerHTML = `<p style="color:#e50914; text-align:center; font-weight:bold;">No anime match found in the database.</p>`;
}
} catch (err) {
resultsBlock.innerHTML = `<p style="color:red; text-align:center;">Error: ${err.message}</p>`;
} finally {
setButtonsDisabled(false);
}
};
// --- Search Feature 2: Tesseract OCR (Movie Poster / Scene Text Extraction) ---
btnPoster.onclick = async () => {
setButtonsDisabled(true);
resultsBlock.innerHTML = `<div style="text-align:center; color:#666;">
<p>⌛ Loading OCR engine...</p>
</div>`;
try {
// Dinamically load Tesseract.js if not available
if (!window.Tesseract) {
await new Promise((resolve, reject) => {
const script = document.createElement("script");
script.src = "https://cdn.jsdelivr.net/npm/tesseract.js@4/dist/tesseract.min.js";
script.onload = resolve;
script.onerror = reject;
document.head.appendChild(script);
});
}
resultsBlock.innerHTML = `<div style="text-align:center; color:#666;">
<p>⌛ Scanning image for text... Please wait.</p>
</div>`;
const result = await window.Tesseract.recognize(canvas, "eng", {
logger: m => {
if (m.status === 'recognizing text') {
const progressIndicator = Math.round(m.progress * 100);
resultsBlock.innerHTML = `<div style="text-align:center; color:#666;">
<p>⌛ Recognizing text... ${progressIndicator}%</p>
</div>`;
}
}
});
const rawText = result.data.text.trim();
if (rawText) {
// Formatting readable query based on dominant text
const cleanText = rawText.replace(/\n+/g, " ").trim();
const keywords = cleanText.split(" ").slice(0, 15).join(" ");
const encodedQuery = encodeURIComponent(keywords.replace(/[^a-zA-Z0-9 ]/g, '')); // Sanitize output format
resultsBlock.innerHTML = `
<h4 style="margin:0 0 12px 0; color:#1a1a2e; font-size:16px;">Extracted Poster Text:</h4>
<div style="background:#f4f5f7; padding:15px; border-radius:8px; margin-bottom:15px; color:#333;">
<em style="font-size:13px; line-height: 1.4;">"${rawText.replace(/\n/g, '<br>')}"</em>
</div>
<h4 style="margin:0 0 10px 0; font-size:14px; color:#1a1a2e; text-align:center;">Search for this movie on:</h4>
<div style="display:flex; gap:10px; font-size:13px;">
<a href="https://www.imdb.com/find?q=${encodedQuery}" target="_blank" style="flex:1; text-align:center; padding:10px; background:#f5c518; color:#000; text-decoration:none; border-radius:6px; font-weight:bold; transition: background 0.2s;" onmouseenter="this.style.background='#d8ad15'" onmouseleave="this.style.background='#f5c518'">IMDB</a>
<a href="https://www.themoviedb.org/search?query=${encodedQuery}" target="_blank" style="flex:1; text-align:center; padding:10px; background:#0d253f; color:#fff; text-decoration:none; border-radius:6px; font-weight:bold; transition: background 0.2s;" onmouseenter="this.style.background='#081829'" onmouseleave="this.style.background='#0d253f'">TMDB</a>
<a href="https://www.google.com/search?q=${encodedQuery}+movie" target="_blank" style="flex:1; text-align:center; padding:10px; background:#4285F4; color:#fff; text-decoration:none; border-radius:6px; font-weight:bold; transition: background 0.2s;" onmouseenter="this.style.background='#3367d6'" onmouseleave="this.style.background='#4285F4'">Google</a>
</div>
`;
} else {
resultsBlock.innerHTML = `<p style="color:#e50914; text-align:center; font-weight:bold;">Could not detect any readable text from the image.</p>`;
}
} catch (err) {
resultsBlock.innerHTML = `<p style="color:red; text-align:center;">Error: ${err.message}</p>`;
} finally {
setButtonsDisabled(false);
}
};
return container;
}
Apply Changes