Please bookmark this page to avoid losing your image tool!

Image Text Drag And Drop By Topic Search Tool

(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, initialTopic = "Earth", textColor = "#FFFFFF", fontSize = 40) {
    const container = document.createElement('div');
    container.style.display = 'flex';
    container.style.flexDirection = 'column';
    container.style.alignItems = 'center';
    container.style.fontFamily = 'Arial, sans-serif';
    container.style.gap = '10px';
    container.style.width = '100%';

    // Toolbox UI
    const toolbox = document.createElement('div');
    toolbox.style.display = 'flex';
    toolbox.style.gap = '10px';
    toolbox.style.padding = '15px';
    toolbox.style.backgroundColor = '#f1f1f1';
    toolbox.style.borderRadius = '8px';
    toolbox.style.boxShadow = '0 2px 4px rgba(0,0,0,0.1)';
    toolbox.style.width = '100%';
    toolbox.style.boxSizing = 'border-box';
    toolbox.style.justifyContent = 'center';
    toolbox.style.flexWrap = 'wrap';

    const input = document.createElement('input');
    input.type = 'text';
    input.value = initialTopic;
    input.placeholder = 'Search topic (e.g. Space, Cats)';
    input.style.padding = '8px 12px';
    input.style.border = '1px solid #ccc';
    input.style.borderRadius = '4px';
    input.style.fontSize = '16px';
    input.style.flexGrow = '1';
    input.style.maxWidth = '300px';

    const btn = document.createElement('button');
    btn.textContent = 'Search & Add Text';
    btn.style.padding = '8px 16px';
    btn.style.backgroundColor = '#007BFF';
    btn.style.color = 'white';
    btn.style.border = 'none';
    btn.style.borderRadius = '4px';
    btn.style.cursor = 'pointer';
    btn.style.fontSize = '16px';
    btn.style.fontWeight = 'bold';
    btn.style.transition = 'background-color 0.2s';

    btn.onmouseover = () => btn.style.backgroundColor = '#0056b3';
    btn.onmouseout = () => btn.style.backgroundColor = '#007BFF';

    const helpText = document.createElement('div');
    helpText.textContent = 'Drag text to move it. Double-click/double-tap an item to remove it.';
    helpText.style.width = '100%';
    helpText.style.textAlign = 'center';
    helpText.style.fontSize = '12px';
    helpText.style.color = '#666';

    toolbox.appendChild(input);
    toolbox.appendChild(btn);
    toolbox.appendChild(helpText);

    // Canvas Wrapper
    const canvasWrapper = document.createElement('div');
    canvasWrapper.style.position = 'relative';
    canvasWrapper.style.maxWidth = '100%';

    const canvas = document.createElement('canvas');
    canvas.width = originalImg.width;
    canvas.height = originalImg.height;
    canvas.style.maxWidth = '100%';
    canvas.style.height = 'auto';
    canvas.style.boxShadow = '0 4px 8px rgba(0,0,0,0.2)';
    canvas.style.borderRadius = '4px';
    
    canvasWrapper.appendChild(canvas);
    container.appendChild(toolbox);
    container.appendChild(canvasWrapper);

    const ctx = canvas.getContext('2d');
    let textBlocks = [];

    // Simple text wrapping logic
    function wrapText(context, text, maxWidth) {
        let words = text.split(' ');
        let lines = [];
        let currentLine = words[0];

        for (let i = 1; i < words.length; i++) {
            let word = words[i];
            let width = context.measureText(currentLine + " " + word).width;
            if (width < maxWidth) {
                currentLine += " " + word;
            } else {
                lines.push(currentLine);
                currentLine = word;
            }
        }
        lines.push(currentLine);
        return lines;
    }

    function draw() {
        // Redraw image
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        ctx.drawImage(originalImg, 0, 0);

        // Setup text styles
        ctx.font = `bold ${fontSize}px Arial, sans-serif`;
        ctx.textAlign = 'left';
        ctx.textBaseline = 'top';
        ctx.fillStyle = textColor;
        ctx.lineWidth = Math.max(2, fontSize * 0.1); 
        ctx.strokeStyle = '#000000'; // Make text legible on any background
        
        ctx.shadowColor = 'rgba(0,0,0,0.8)';
        ctx.shadowBlur = 5;
        ctx.shadowOffsetX = 2;
        ctx.shadowOffsetY = 2;

        for (let block of textBlocks) {
            if (!block.lines) {
                block.lines = wrapText(ctx, block.text, canvas.width * 0.8);
                block.width = 0;
                block.lines.forEach(l => {
                    let w = ctx.measureText(l).width;
                    if (w > block.width) block.width = w;
                });
                block.height = block.lines.length * (fontSize * 1.2);
            }

            block.lines.forEach((line, index) => {
                const lineY = block.y + index * (fontSize * 1.2);
                ctx.strokeText(line, block.x, lineY);
                ctx.fillText(line, block.x, lineY);
            });
        }
        
        // Reset shadow to avoid affecting anything else later
        ctx.shadowColor = 'transparent';
    }

    // Initial draw call
    draw();

    // Interaction State
    let draggingBlock = null;
    let dragOffsetX = 0;
    let dragOffsetY = 0;
    let lastClickTime = 0;

    function getMousePos(e) {
        const rect = canvas.getBoundingClientRect();
        const scaleX = canvas.width / rect.width;
        const scaleY = canvas.height / rect.height;
        return {
            x: (e.clientX - rect.left) * scaleX,
            y: (e.clientY - rect.top) * scaleY
        };
    }

    function getBlockAtPos(pos) {
        // Reverse array checking brings the topmost item first (the one written last)
        for (let i = textBlocks.length - 1; i >= 0; i--) {
            let block = textBlocks[i];
            if (pos.x >= block.x - 10 && pos.x <= block.x + block.width + 10 &&
                pos.y >= block.y - 10 && pos.y <= block.y + block.height + 10) {
                return { block, index: i };
            }
        }
        return null;
    }

    // Mouse Listeners
    canvas.addEventListener('mousedown', (e) => {
        const pos = getMousePos(e);
        const found = getBlockAtPos(pos);
        
        if (found) {
            const currentTime = Date.now();
            // Double click removal handling
            if (currentTime - lastClickTime < 300) {
                textBlocks.splice(found.index, 1);
                draw();
                lastClickTime = 0;
                return;
            }
            lastClickTime = currentTime;

            draggingBlock = found.block;
            dragOffsetX = pos.x - found.block.x;
            dragOffsetY = pos.y - found.block.y;
            
            // Bring item to the front layer
            textBlocks.splice(found.index, 1);
            textBlocks.push(found.block);
            
            draw();
        }
    });

    canvas.addEventListener('mousemove', (e) => {
        const pos = getMousePos(e);
        
        if (draggingBlock) {
            draggingBlock.x = pos.x - dragOffsetX;
            draggingBlock.y = pos.y - dragOffsetY;
            draw();
        } else {
            const found = getBlockAtPos(pos);
            canvas.style.cursor = found ? 'move' : 'default';
        }
    });

    canvas.addEventListener('mouseup', () => draggingBlock = null);
    canvas.addEventListener('mouseleave', () => draggingBlock = null);

    // Touch support hooks for Mobile compatibility
    canvas.addEventListener('touchstart', (e) => {
        if (e.touches.length > 0) {
            const touch = e.touches[0];
            const pos = getMousePos(touch);
            const found = getBlockAtPos(pos);
            if (found) {
                e.preventDefault(); 
                
                const currentTime = Date.now();
                if (currentTime - lastClickTime < 300) {
                    textBlocks.splice(found.index, 1);
                    draw();
                    lastClickTime = 0;
                    return;
                }
                lastClickTime = currentTime;

                draggingBlock = found.block;
                dragOffsetX = pos.x - found.block.x;
                dragOffsetY = pos.y - found.block.y;
                
                textBlocks.splice(found.index, 1);
                textBlocks.push(found.block);
                draw();
            }
        }
    }, { passive: false });

    canvas.addEventListener('touchmove', (e) => {
        if (draggingBlock && e.touches.length > 0) {
            e.preventDefault();
            const pos = getMousePos(e.touches[0]);
            draggingBlock.x = pos.x - dragOffsetX;
            draggingBlock.y = pos.y - dragOffsetY;
            draw();
        }
    }, { passive: false });

    canvas.addEventListener('touchend', () => draggingBlock = null);
    canvas.addEventListener('touchcancel', () => draggingBlock = null);

    // Wikipedia Fetching Logic
    async function addTopicText(topic) {
        btn.textContent = 'Searching...';
        btn.disabled = true;
        let textStr = topic;

        try {
            const url = `https://en.wikipedia.org/w/api.php?action=query&prop=extracts&exsentences=1&exlimit=1&titles=${encodeURIComponent(topic)}&explaintext=1&formatversion=2&origin=*&format=json`;
            const res = await fetch(url);
            const data = await res.json();
            
            if (data.query && data.query.pages && data.query.pages[0].extract) {
                const extract = data.query.pages[0].extract.trim();
                textStr = extract ? extract : `No summary available for "${topic}".`;
            } else {
                textStr = `No data found for "${topic}".`;
            }
        } catch(err) {
            textStr = `Error fetching topic: ${topic}`;
        }
        
        btn.textContent = 'Search & Add Text';
        btn.disabled = false;

        ctx.font = `bold ${fontSize}px Arial, sans-serif`;
        textBlocks.push({
            id: Date.now(),
            text: textStr,
            x: canvas.width * 0.1,
            y: canvas.height * 0.1 + (textBlocks.length * (fontSize * 1.5)),
            lines: null,
            width: 0,
            height: 0
        });
        
        draw();
    }

    btn.addEventListener('click', () => {
        if (input.value.trim()) {
            addTopicText(input.value.trim());
        }
    });

    input.addEventListener('keypress', (e) => {
        if (e.key === 'Enter') {
            e.preventDefault();
            btn.click();
        }
    });

    // Run automatically on load if string is present
    if (initialTopic) {
        addTopicText(initialTopic);
    }

    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

This tool allows you to overlay informative text onto your images by searching for specific topics. By entering a keyword, the tool fetches a brief summary from Wikipedia and adds it as a text block on your image. You can interactively customize the composition by dragging text blocks to different positions or double-clicking them to remove them. This is useful for creating educational graphics, social media content, or visual aids that combine imagery with factual information.

Leave a Reply

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