You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(originalImg, maxTopics = 5, buttonColor = "#4285F4") {
// Create Main Container Wrapper
const container = document.createElement('div');
container.style.fontFamily = 'system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif';
container.style.textAlign = 'center';
container.style.padding = '24px';
container.style.backgroundColor = '#ffffff';
container.style.borderRadius = '12px';
container.style.boxShadow = '0 4px 12px rgba(0,0,0,0.15)';
container.style.maxWidth = '600px';
container.style.margin = '0 auto';
// Title
const title = document.createElement('h2');
title.innerText = 'Google Search Topic Finder';
title.style.margin = '0 0 16px 0';
title.style.color = '#202124';
title.style.fontSize = '22px';
container.appendChild(title);
// Description
const desc = document.createElement('p');
desc.innerText = 'Название похожие / Identifying image topics...';
desc.style.margin = '0 0 20px 0';
desc.style.color = '#5f6368';
desc.style.fontSize = '14px';
container.appendChild(desc);
// Display Image
const imgContainer = document.createElement('div');
imgContainer.style.marginBottom = '20px';
const img = new Image();
img.crossOrigin = 'anonymous'; // Helpful for TFJS if image is external
img.src = originalImg.src;
img.style.maxWidth = '100%';
img.style.maxHeight = '300px';
img.style.borderRadius = '8px';
img.style.boxShadow = '0 2px 6px rgba(0,0,0,0.1)';
img.style.objectFit = 'contain';
imgContainer.appendChild(img);
container.appendChild(imgContainer);
// Status/Loading Text
const statusText = document.createElement('div');
statusText.innerHTML = `<span>⏳ Analyzing image with AI... please wait.</span>`;
statusText.style.color = '#5f6368';
statusText.style.fontSize = '15px';
statusText.style.fontWeight = '500';
statusText.style.padding = '10px';
container.appendChild(statusText);
// Dynamic script loading function
const loadScript = (src, globalVarToCheck) => {
return new Promise((resolve, reject) => {
if (window[globalVarToCheck]) {
resolve();
return;
}
const script = document.createElement('script');
script.src = src;
script.onload = resolve;
script.onerror = reject;
document.head.appendChild(script);
});
};
// Process the image lazily to allow UI to render first
setTimeout(async () => {
try {
// 1. Load TensorFlow.js core
await loadScript('https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@latest', 'tf');
// 2. Load MobileNet model for image classification
await loadScript('https://cdn.jsdelivr.net/npm/@tensorflow-models/mobilenet@latest', 'mobilenet');
// 3. Load the model and classify
const model = await window.mobilenet.load();
const predictions = await model.classify(img);
// Prepare UI for topics
statusText.innerHTML = '✨ <strong>Suggested Search Topics (Название):</strong>';
statusText.style.color = '#202124';
const topicsContainer = document.createElement('div');
topicsContainer.style.display = 'flex';
topicsContainer.style.flexWrap = 'wrap';
topicsContainer.style.gap = '10px';
topicsContainer.style.justifyContent = 'center';
topicsContainer.style.marginTop = '15px';
// Extract raw labels from predictions
let extractedTopics = [];
predictions.forEach(p => {
// MobileNet returns classes as comma-separated values (e.g., "golden retriever, dog")
const classes = p.className.split(',').map(s => s.trim());
extractedTopics.push(...classes);
});
// Filter uniques and limit number of topics
extractedTopics = [...new Set(extractedTopics)];
const numTopics = isNaN(Number(maxTopics)) ? 5 : Number(maxTopics);
extractedTopics = extractedTopics.slice(0, numTopics);
if (extractedTopics.length === 0) {
statusText.innerText = 'Could not identify specific topics for this image.';
} else {
// Build interactive topic buttons
extractedTopics.forEach((topic) => {
const btn = document.createElement('a');
// Generate an active Google search link for the topic
btn.href = `https://www.google.com/search?q=${encodeURIComponent(topic)}`;
btn.target = '_blank';
btn.innerText = `🔍 ${topic}`;
Object.assign(btn.style, {
padding: '10px 20px',
backgroundColor: buttonColor,
color: '#ffffff',
textDecoration: 'none',
borderRadius: '24px',
fontSize: '14px',
fontWeight: '600',
boxShadow: '0 2px 4px rgba(0,0,0,0.2)',
transition: 'transform 0.2s, background-color 0.2s, box-shadow 0.2s',
cursor: 'pointer',
display: 'inline-block'
});
btn.onmouseover = () => {
btn.style.transform = 'translateY(-2px)';
btn.style.boxShadow = '0 4px 8px rgba(0,0,0,0.3)';
btn.style.opacity = '0.9';
};
btn.onmouseout = () => {
btn.style.transform = 'translateY(0)';
btn.style.boxShadow = '0 2px 4px rgba(0,0,0,0.2)';
btn.style.opacity = '1';
};
topicsContainer.appendChild(btn);
});
}
container.appendChild(topicsContainer);
} catch (error) {
statusText.innerHTML = `⚠️ <strong>Error analyzing image:</strong> ${error.message}<br>Please ensure the image doesn't violate CORS policies or try a different image.`;
statusText.style.color = '#ea4335';
}
}, 100);
return container;
}
Apply Changes