You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(originalImg, maxResults = 3, themeColor = "#e50914") {
// Determine the number of results to display
const numResults = typeof maxResults === 'string' ? parseInt(maxResults, 10) : maxResults;
// Create the primary canvas
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
const width = originalImg.width;
const height = originalImg.height;
canvas.width = width;
canvas.height = height;
// Function to draw a loading state
const drawLoading = () => {
ctx.clearRect(0, 0, width, height);
ctx.drawImage(originalImg, 0, 0, width, height);
ctx.fillStyle = "rgba(0, 0, 0, 0.7)";
ctx.fillRect(0, 0, width, height);
const fontSize = Math.max(16, Math.floor(width * 0.04));
ctx.fillStyle = themeColor;
ctx.font = `bold ${fontSize}px "Courier New", Courier, monospace`;
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillText("INITIALIZING AI SYSTEM...", width / 2, height / 2 - fontSize);
ctx.fillStyle = "#ffffff";
ctx.font = `${Math.max(12, fontSize * 0.7)}px "Courier New", Courier, monospace`;
ctx.fillText("Searching topics...", width / 2, height / 2 + fontSize);
};
drawLoading();
// Helper to dynamically load external scripts safely
const loadScript = (src, globalVar) => {
return new Promise((resolve, reject) => {
if (window[globalVar]) {
resolve();
return;
}
const script = document.createElement("script");
script.src = src;
script.crossOrigin = "anonymous";
script.onload = () => resolve();
script.onerror = () => reject(new Error(`Failed to load ${src}`));
document.head.appendChild(script);
});
};
let predictions = [];
try {
// Load TensorFlow.js core
await loadScript("https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@latest", "tf");
// Load MobileNet model
await loadScript("https://cdn.jsdelivr.net/npm/@tensorflow-models/mobilenet@latest", "mobilenet");
// Load model and classify directly from the original image element
const model = await window.mobilenet.load();
predictions = await model.classify(originalImg);
} catch (error) {
console.error("AI Classification failed:", error);
predictions = [{ className: "Error: Unable to connect to AI server", probability: 0 }];
}
// Reset canvas and draw the original image
ctx.drawImage(originalImg, 0, 0, width, height);
// Add a cinematic vignette effect
const gradient = ctx.createRadialGradient(width / 2, height / 2, width / 4, width / 2, height / 2, Math.max(width, height) / 1.1);
gradient.addColorStop(0, "rgba(0, 0, 0, 0)");
gradient.addColorStop(1, "rgba(0, 0, 0, 0.65)");
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, width, height);
// Draw cinematic letterbars (Movie aesthetic)
const barHeight = Math.max(50, height * 0.12);
ctx.fillStyle = "#030303";
ctx.fillRect(0, 0, width, barHeight); // Top bar
ctx.fillRect(0, height - barHeight, width, barHeight); // Bottom bar
// Top Bar UI items (Recording indicator & Title)
const topFontSize = Math.max(14, barHeight * 0.35);
ctx.font = `bold ${topFontSize}px "Courier New", Courier, monospace`;
ctx.textBaseline = "middle";
ctx.textAlign = "left";
// Draw recording dot
const dotRadius = topFontSize * 0.4;
ctx.fillStyle = "#ff0000";
ctx.beginPath();
ctx.arc(20 + dotRadius, barHeight / 2, dotRadius, 0, Math.PI * 2);
ctx.fill();
ctx.fillStyle = "#ffffff";
ctx.fillText("REC", 30 + dotRadius * 2, barHeight / 2 + 1);
// Draw Title
ctx.textAlign = "center";
ctx.fillStyle = themeColor;
ctx.fillText("MOVIE SEARCH TOPIC IDENTIFIER", width / 2, barHeight / 2);
// Draw Timecode on right
const timecode = new Date().toISOString().substring(11, 23);
ctx.textAlign = "right";
ctx.fillStyle = "#ffffff";
ctx.fillText(timecode, width - 20, barHeight / 2);
// Bottom Bar UI items (Predictions & Confidence)
const bottomBaseSize = Math.max(12, barHeight * 0.3);
const textPadding = 20;
// Title for results area
ctx.font = `bold ${bottomBaseSize}px sans-serif`;
ctx.textAlign = "left";
ctx.fillStyle = themeColor;
ctx.fillText("► IDENTIFIED TOPICS:", textPadding, height - barHeight + (barHeight * 0.35));
// Process and draw the tags
ctx.font = `bold ${bottomBaseSize * 0.85}px sans-serif`;
ctx.fillStyle = "#ffffff";
// Filter and format tags
const displayResults = predictions.slice(0, numResults).map(p => {
const primaryClass = p.className.split(",")[0].toUpperCase();
const confidence = Math.round(p.probability * 100);
return `[ ${primaryClass} ${confidence}% ]`;
});
const tagsString = displayResults.join(" ");
// Simple logic to prevent overflowing text
// If it overflows, it might trail off instead of wrapping for simplicity in standard canvas usage
ctx.fillText(tagsString, textPadding, height - (barHeight * 0.3));
// Draw viewfinder crosshairs
const cSize = Math.min(width, height) * 0.05;
ctx.strokeStyle = "rgba(255, 255, 255, 0.4)";
ctx.lineWidth = 2;
ctx.beginPath();
// Center horizontal
ctx.moveTo(width / 2 - cSize, height / 2);
ctx.lineTo(width / 2 + cSize, height / 2);
// Center vertical
ctx.moveTo(width / 2, height / 2 - cSize);
ctx.lineTo(width / 2, height / 2 + cSize);
ctx.stroke();
return canvas;
}
Apply Changes