You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, themeColor = "#00FF41", fontSize = "auto") {
// Create the main canvas that will be returned
const canvas = document.createElement('canvas');
canvas.width = originalImg.width;
canvas.height = originalImg.height;
const ctx = canvas.getContext('2d');
// Scanning animation variables
let isScanning = true;
let scanY = 0;
let scanDir = 1;
// Calculate a reasonable speed relative to the image size
const scanSpeed = Math.max(2, canvas.height / 60);
// Start the visual scanning loop
const animate = () => {
if (!isScanning) return;
// Redraw original image to clear previous scan lines
ctx.drawImage(originalImg, 0, 0);
// Draw green trailing scan effect
ctx.fillStyle = themeColor;
ctx.globalAlpha = 0.15;
ctx.fillRect(0, 0, canvas.width, scanY);
ctx.globalAlpha = 1.0;
// Draw main scan line
ctx.strokeStyle = themeColor;
ctx.lineWidth = 3;
ctx.beginPath();
ctx.moveTo(0, scanY);
ctx.lineTo(canvas.width, scanY);
ctx.stroke();
// Overlay status text
const textBgWidth = 230;
ctx.fillStyle = "rgba(0, 0, 0, 0.8)";
ctx.fillRect(10, 10, textBgWidth, 36);
ctx.fillStyle = themeColor;
ctx.font = "14px monospace";
ctx.fillText("SCANNING M.E.D.I.A...", 20, 33);
// Advance scanline
scanY += scanDir * scanSpeed;
if (scanY > canvas.height || scanY < 0) {
scanDir *= -1; // Reverse direction upon hitting the edges
}
// Loop
requestAnimationFrame(animate);
};
// Kick off animation
animate();
// Helper to dynamically load external scripts without duplication
const loadScript = (src) => new Promise((resolve, reject) => {
if (document.querySelector(`script[src="${src}"]`)) {
return resolve();
}
const script = document.createElement('script');
script.src = src;
script.crossOrigin = "anonymous";
script.onload = resolve;
script.onerror = reject;
document.head.appendChild(script);
});
// Run Object Identification & Topic Picking in the background
(async () => {
try {
// Dynamically load TensorFlow.js and the MobileNet model architecture
if (!window.tf) {
await loadScript("https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@3.21.0/dist/tf.min.js");
}
if (!window.mobilenet) {
await loadScript("https://cdn.jsdelivr.net/npm/@tensorflow-models/mobilenet@2.1.0/dist/mobilenet.min.js");
}
// Load the image classification model
const model = await window.mobilenet.load();
// Generate predictions (passing originalImg allows TF to use underlying pixel data properly)
const predictions = await model.classify(originalImg);
// Stop scanning effect visual loop
isScanning = false;
// Clear animation noise
ctx.drawImage(originalImg, 0, 0);
// Determine dimensions for typography and layouts based on image
const fSize = fontSize === "auto" ? Math.max(14, Math.min(28, canvas.width * 0.04)) : Number(fontSize);
// Get highest prediction
const topResult = predictions && predictions.length > 0 ? predictions[0] : { className: "UNKNOWN ENTITY", probability: 0 };
// Pick a singular mediateka topic (MobileNet groups by commas usually, so strip the primary class)
const topics = topResult.className.split(',').map(s => s.trim());
const mediatekaTopic = topics[0].toUpperCase();
// Draw a techy/Identifier targeting UI
const cLen = Math.max(20, Math.min(canvas.width, canvas.height) * 0.1);
ctx.strokeStyle = themeColor;
ctx.lineWidth = 4;
const offset = 15;
// Target corners (Top-Left, Top-Right, Bottom-Left, Bottom-Right)
ctx.beginPath(); ctx.moveTo(offset, offset + cLen); ctx.lineTo(offset, offset); ctx.lineTo(offset + cLen, offset); ctx.stroke();
ctx.beginPath(); ctx.moveTo(canvas.width - offset - cLen, offset); ctx.lineTo(canvas.width - offset, offset); ctx.lineTo(canvas.width - offset, offset + cLen); ctx.stroke();
ctx.beginPath(); ctx.moveTo(offset, canvas.height - offset - cLen); ctx.lineTo(offset, canvas.height - offset); ctx.lineTo(offset + cLen, canvas.height - offset); ctx.stroke();
ctx.beginPath(); ctx.moveTo(canvas.width - offset - cLen, canvas.height - offset); ctx.lineTo(canvas.width - offset, canvas.height - offset); ctx.lineTo(canvas.width - offset, canvas.height - offset - cLen); ctx.stroke();
// Draw Data Readout / Result Box at the bottom
const boxHeight = fSize * 4;
const boxY = canvas.height - offset - boxHeight;
ctx.fillStyle = "rgba(10, 15, 10, 0.85)";
ctx.fillRect(offset, boxY, canvas.width - (offset * 2), boxHeight);
ctx.lineWidth = 2;
ctx.strokeRect(offset, boxY, canvas.width - (offset * 2), boxHeight);
// Print Header
ctx.fillStyle = "#FFFFFF";
ctx.font = `bold ${fSize * 0.85}px monospace`;
ctx.fillText("MEDIATEKA SEARCH TOPIC:", offset + 15, boxY + fSize * 1.3);
// Print the Chosen Topic Pick
ctx.fillStyle = themeColor;
ctx.font = `bold ${fSize * 1.3}px monospace`;
// Ensure maximum length to prevent clipping off bounds
ctx.fillText(mediatekaTopic, offset + 15, boxY + fSize * 2.7);
// Print the Scanner's confidence interval
ctx.fillStyle = "#AAAAAA";
ctx.font = `${fSize * 0.75}px monospace`;
ctx.fillText(`IDENTIFIER CONFIDENCE: ${(topResult.probability * 100).toFixed(2)}%`, offset + 15, boxY + fSize * 3.6);
} catch (err) {
// Handle Loading or Processing Errors cleanly visually too
isScanning = false;
ctx.drawImage(originalImg, 0, 0);
ctx.fillStyle = "rgba(255, 0, 0, 0.8)";
ctx.fillRect(0, 0, canvas.width, 60);
ctx.fillStyle = "#FFFFFF";
ctx.font = "16px monospace";
ctx.fillText("SCAN ERROR: " + err.message, 10, 35);
}
})();
// Synchronously returns the Canvas Element immediately so it can be appended,
// while processing asynchronously overlays on it in realtime.
return canvas;
}
Apply Changes