Please bookmark this page to avoid losing your image tool!

Image Drag And Drop Textbox Adder

(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.
function processImage(originalImg, defaultText = "Add Text!", defaultColor = "#ffffff", defaultSize = 40) {
    // Main wrapper container
    const container = document.createElement('div');
    container.style.fontFamily = 'Arial, sans-serif';
    container.style.display = 'inline-block';
    container.style.maxWidth = '100%';

    // Toolbar for controls
    const toolbar = document.createElement('div');
    toolbar.style.padding = '10px';
    toolbar.style.backgroundColor = '#f4f4f4';
    toolbar.style.border = '1px solid #ddd';
    toolbar.style.borderBottom = 'none';
    toolbar.style.display = 'flex';
    toolbar.style.flexWrap = 'wrap';
    toolbar.style.gap = '15px';
    toolbar.style.alignItems = 'center';
    toolbar.style.borderTopLeftRadius = '8px';
    toolbar.style.borderTopRightRadius = '8px';

    const addBtn = document.createElement('button');
    addBtn.innerText = '+ Add Textbox';
    addBtn.style.padding = '5px 10px';
    addBtn.style.cursor = 'pointer';

    const colorContainer = document.createElement('div');
    colorContainer.innerHTML = '<span style="font-size: 14px;">Color: </span>';
    const colorInput = document.createElement('input');
    colorInput.type = 'color';
    colorInput.value = defaultColor;
    colorInput.style.cursor = 'pointer';
    colorContainer.appendChild(colorInput);

    const sizeContainer = document.createElement('div');
    sizeContainer.innerHTML = '<span style="font-size: 14px;">Size: </span>';
    const sizeInput = document.createElement('input');
    sizeInput.type = 'number';
    sizeInput.value = defaultSize;
    sizeInput.style.width = '60px';
    sizeInput.style.padding = '3px';
    sizeContainer.appendChild(sizeInput);

    const flattenBtn = document.createElement('button');
    flattenBtn.innerText = 'Flatten Image';
    flattenBtn.style.padding = '5px 15px';
    flattenBtn.style.fontWeight = 'bold';
    flattenBtn.style.backgroundColor = '#4CAF50';
    flattenBtn.style.color = 'white';
    flattenBtn.style.border = 'none';
    flattenBtn.style.borderRadius = '4px';
    flattenBtn.style.cursor = 'pointer';
    flattenBtn.style.marginLeft = 'auto';

    toolbar.appendChild(addBtn);
    toolbar.appendChild(colorContainer);
    toolbar.appendChild(sizeContainer);
    toolbar.appendChild(flattenBtn);
    container.appendChild(toolbar);

    // Editing canvas and area
    const workArea = document.createElement('div');
    workArea.style.position = 'relative';
    workArea.style.display = 'inline-block';
    workArea.style.border = '1px solid #ddd';
    workArea.style.maxWidth = '100%';
    workArea.style.overflow = 'hidden'; // Ensure elements don't spill outside

    // Backing canvas to allow flattening
    const canvas = document.createElement('canvas');
    canvas.width = originalImg.width;
    canvas.height = originalImg.height;
    canvas.style.display = 'block';
    canvas.style.maxWidth = '100%';
    canvas.style.height = 'auto';
    
    const ctx = canvas.getContext('2d');
    ctx.drawImage(originalImg, 0, 0);

    workArea.appendChild(canvas);
    container.appendChild(workArea);

    const textBoxes = [];

    // Helper to extract newlines split correctly
    const createTextbox = () => {
        const box = document.createElement('div');
        box.style.position = 'absolute';
        box.style.left = '50%';
        box.style.top = '50%';
        box.style.transform = 'translate(-50%, -50%)'; // Center initially
        box.style.display = 'inline-block';

        // Drag handle for easy grabbing
        const handle = document.createElement('div');
        handle.innerHTML = '&#10021;'; // Cross arrow symbol
        handle.style.position = 'absolute';
        handle.style.left = '-12px';
        handle.style.top = '-12px';
        handle.style.width = '24px';
        handle.style.height = '24px';
        handle.style.backgroundColor = '#007BFF';
        handle.style.color = 'white';
        handle.style.borderRadius = '50%';
        handle.style.textAlign = 'center';
        handle.style.lineHeight = '24px';
        handle.style.fontSize = '14px';
        handle.style.cursor = 'move';
        handle.style.zIndex = '10';
        handle.style.boxShadow = '0 2px 4px rgba(0,0,0,0.3)';
        handle.style.opacity = '0';
        handle.style.transition = 'opacity 0.2s';
        box.appendChild(handle);

        const textNode = document.createElement('div');
        textNode.contentEditable = true;
        textNode.innerText = defaultText;
        textNode.style.color = colorInput.value;
        textNode.style.fontSize = sizeInput.value + 'px';
        textNode.style.fontFamily = 'Arial, sans-serif';
        textNode.style.fontWeight = 'bold';
        // Stroke effect
        textNode.style.textShadow = '2px 2px 0 #000, -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000';
        textNode.style.outline = 'none';
        textNode.style.whiteSpace = 'pre'; // Force manual linebreaks to preserve exact structural alignment with canvas
        textNode.style.minWidth = '20px';
        textNode.style.minHeight = '30px';
        textNode.style.padding = '5px';
        textNode.style.border = '1px dashed transparent';
        
        box.appendChild(textNode);
        workArea.appendChild(box);
        textBoxes.push({ box, textNode });

        // Show/Hide drag handle on hover
        box.addEventListener('mouseenter', () => { handle.style.opacity = '1'; textNode.style.border = '1px dashed #ccc'; });
        box.addEventListener('mouseleave', () => { handle.style.opacity = '0'; textNode.style.border = '1px dashed transparent'; });

        // Dragging Logic
        let isDragging = false;
        let startX, startY, initialLeft, initialTop;

        handle.addEventListener('mousedown', (e) => {
            isDragging = true;
            startX = e.clientX;
            startY = e.clientY;

            // Convert transform centering into absolute positioning relative to parent to make dragging smooth
            if (box.style.transform) {
                const rect = box.getBoundingClientRect();
                const parentRect = workArea.getBoundingClientRect();
                box.style.transform = 'none';
                box.style.left = (rect.left - parentRect.left) + 'px';
                box.style.top = (rect.top - parentRect.top) + 'px';
            }

            initialLeft = parseFloat(box.style.left) || 0;
            initialTop = parseFloat(box.style.top) || 0;
            e.preventDefault(); // prevent unwanted selections

            const onMouseMove = (ev) => {
                if (!isDragging) return;
                const dx = ev.clientX - startX;
                const dy = ev.clientY - startY;
                box.style.left = (initialLeft + dx) + 'px';
                box.style.top = (initialTop + dy) + 'px';
            };

            const onMouseUp = () => {
                isDragging = false;
                document.removeEventListener('mousemove', onMouseMove);
                document.removeEventListener('mouseup', onMouseUp);
            };

            document.addEventListener('mousemove', onMouseMove);
            document.addEventListener('mouseup', onMouseUp);
        });

        // Removal logic if emptied out
        textNode.addEventListener('blur', () => {
            if (textNode.innerText.trim() === '') {
                box.remove();
                const idx = textBoxes.findIndex(t => t.box === box);
                if (idx > -1) textBoxes.splice(idx, 1);
            }
        });
        
        // Auto-select text on generation
        setTimeout(() => textNode.focus(), 50);
    };

    addBtn.addEventListener('click', createTextbox);

    flattenBtn.addEventListener('click', () => {
        // Calculate scaling ratio if canvas is compressed via CSS max-width
        const workRect = workArea.getBoundingClientRect();
        const ratioX = canvas.width / workRect.width;
        const ratioY = canvas.height / workRect.height;

        textBoxes.forEach(({ box, textNode }) => {
            const nodeRect = textNode.getBoundingClientRect();
            const canvasRect = canvas.getBoundingClientRect();

            // Calculate exact starting position scaled to natural canvas dimensions
            const startX = (nodeRect.left - canvasRect.left + 5) * ratioX; // +5 intercepts DOM padding compensation
            const startY = (nodeRect.top - canvasRect.top + 5) * ratioY;

            const fontSize = parseFloat(textNode.style.fontSize) * Math.min(ratioX, ratioY);
            
            ctx.font = `bold ${fontSize}px Arial, sans-serif`;
            ctx.fillStyle = textNode.style.color;
            ctx.textAlign = 'left';
            ctx.textBaseline = 'top';

            // Meme-style heavy stroke
            ctx.lineJoin = 'round';
            ctx.lineWidth = Math.max(3, fontSize / 8);
            ctx.strokeStyle = '#000000';

            const lines = textNode.innerText.split('\n');
            let currentY = startY;

            lines.forEach(line => {
                ctx.strokeText(line, startX, currentY);
                ctx.fillText(line, startX, currentY);
                currentY += fontSize * 1.15; // standard DOM line-height approximation
            });

            box.remove();
        });

        textBoxes.length = 0; // Reset array
        
        // Clean layout down to just the finished image
        container.innerHTML = '';
        canvas.style.borderRadius = '8px';
        canvas.style.boxShadow = '0 4px 8px rgba(0,0,0,0.1)';
        container.appendChild(canvas);
    });

    // Populate the first textbox
    createTextbox();

    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

The Image Drag and Drop Textbox Adder is a web-based image editing tool that allows you to overlay custom text onto your images. Users can add multiple textboxes, customize the text color and font size, and easily reposition text anywhere on the image using a drag-and-drop interface. The tool features a ‘flatten’ function that merges the text with the original image, creating a single, permanent file. This tool is ideal for creating memes, adding captions to photos, watermarking images, or designing simple social media graphics.

Leave a Reply

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