Please bookmark this page to avoid losing your image tool!

Two Photo Comparison 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.
/**
 * Two Photo Comparison Search Tool
 * Compares two images using an interactive Before/After slider, a visual diff, or side-by-side mode.
 * 
 * @param {HTMLImageElement} originalImg - The primary image (Before/Original).
 * @param {string} secondImageURL - Base64 Image URL or Data URL of the second image (After/Target). Defaults to an auto-generated grayscale image if empty.
 * @param {string} comparisonMode - The display mode: "slider", "diff", or "side-by-side". Default is "slider".
 * @returns {Promise<HTMLElement | HTMLCanvasElement>} A DOM element (div or canvas) containing the visualization.
 */
async function processImage(originalImg, secondImageURL = "", comparisonMode = "slider") {
    const width = originalImg.width || 800;
    const height = originalImg.height || 600;
    
    comparisonMode = comparisonMode.toLowerCase().trim();

    // Load the second image
    const img2 = new Image();
    img2.crossOrigin = "Anonymous";

    await new Promise((resolve) => {
        if (!secondImageURL) {
            // Generate a grayscale fallback of the original image for demonstration purposes
            const tempCanvas = document.createElement('canvas');
            tempCanvas.width = width;
            tempCanvas.height = height;
            const tempCtx = tempCanvas.getContext('2d');
            tempCtx.drawImage(originalImg, 0, 0);
            const tData = tempCtx.getImageData(0, 0, width, height);
            
            for(let i = 0; i < tData.data.length; i += 4) {
                let gray = (tData.data[i] + tData.data[i+1] + tData.data[i+2]) / 3;
                tData.data[i] = gray;     // R
                tData.data[i+1] = gray;   // G
                tData.data[i+2] = gray;   // B
            }
            tempCtx.putImageData(tData, 0, 0);
            img2.src = tempCanvas.toDataURL();
            resolve();
        } else {
            img2.onload = resolve;
            img2.onerror = () => {
                console.warn("Failed to load the second image. Falling back to the original image.");
                img2.src = originalImg.src;
                resolve();
            };
            img2.src = secondImageURL;
        }
    });

    // MODE 1: Difference Highlighter (Highlights changed pixels)
    if (comparisonMode === "diff") {
        const canvas = document.createElement('canvas');
        canvas.width = width;
        canvas.height = height;
        const ctx = canvas.getContext('2d');

        ctx.drawImage(originalImg, 0, 0, width, height);
        const imgData1 = ctx.getImageData(0, 0, width, height);

        ctx.clearRect(0, 0, width, height);
        ctx.drawImage(img2, 0, 0, width, height);
        const imgData2 = ctx.getImageData(0, 0, width, height);

        const outData = ctx.createImageData(width, height);
        for (let i = 0; i < imgData1.data.length; i += 4) {
            const r1 = imgData1.data[i], g1 = imgData1.data[i+1], b1 = imgData1.data[i+2], a1 = imgData1.data[i+3];
            const r2 = imgData2.data[i], g2 = imgData2.data[i+1], b2 = imgData2.data[i+2], a2 = imgData2.data[i+3];
            
            const dr = Math.abs(r1 - r2);
            const dg = Math.abs(g1 - g2);
            const db = Math.abs(b1 - b2);
            
            // Calculate absolute difference magnitude
            const diff = dr + dg + db;

            if (diff > 20 || Math.abs(a1 - a2) > 20) {
                // Highlight modified pixels in vivid red to aid the "search tool" discovery
                outData.data[i] = 255; 
                outData.data[i+1] = 0; 
                outData.data[i+2] = 0; 
                outData.data[i+3] = 255;
            } else {
                // Return dim grayscale version of unchanged pixels
                const gray = 0.3 * r1 + 0.59 * g1 + 0.11 * b1;
                outData.data[i] = gray * 0.4;
                outData.data[i+1] = gray * 0.4;
                outData.data[i+2] = gray * 0.4;
                outData.data[i+3] = 255;
            }
        }
        ctx.putImageData(outData, 0, 0);
        return canvas;
    }

    // MODE 2: Side-By-Side Canvas
    if (comparisonMode === "side-by-side") {
        const canvas = document.createElement('canvas');
        canvas.width = width * 2;
        canvas.height = height;
        const ctx = canvas.getContext('2d');
        ctx.drawImage(originalImg, 0, 0, width, height);
        ctx.drawImage(img2, width, 0, width, height);
        
        // Visual separator
        ctx.fillStyle = '#FFFFFF';
        ctx.fillRect(width - 2, 0, 4, height);
        ctx.fillStyle = '#000000';
        ctx.fillRect(width - 1, 0, 2, height);
        
        return canvas;
    }

    // MODE 3: Interactive Before & After Slider Element
    const container = document.createElement('div');
    container.style.position = 'relative';
    container.style.display = 'block';
    container.style.width = '100%';
    container.style.maxWidth = width + 'px';
    container.style.aspectRatio = `${width} / ${height}`;
    container.style.overflow = 'hidden';
    container.style.userSelect = 'none';
    container.style.backgroundColor = '#000';
    container.style.boxShadow = '0 4px 10px rgba(0,0,0,0.1)';
    container.style.borderRadius = '4px';

    const createLabel = (text, isLeft) => {
        const lbl = document.createElement('div');
        lbl.textContent = text;
        lbl.style.position = 'absolute';
        lbl.style.top = '10px';
        lbl.style.padding = '4px 8px';
        lbl.style.background = 'rgba(0,0,0,0.6)';
        lbl.style.color = '#fff';
        lbl.style.fontFamily = 'system-ui, sans-serif';
        lbl.style.fontSize = '12px';
        lbl.style.fontWeight = 'bold';
        lbl.style.borderRadius = '4px';
        lbl.style.pointerEvents = 'none';
        lbl.style.zIndex = '5';
        if (isLeft) lbl.style.left = '10px';
        else lbl.style.right = '10px';
        return lbl;
    };

    // Right / Bottom Image Layer
    const wrapper2 = document.createElement('div');
    wrapper2.style.position = 'absolute';
    wrapper2.style.top = '0';
    wrapper2.style.left = '0';
    wrapper2.style.width = '100%';
    wrapper2.style.height = '100%';
    
    const img2El = new Image();
    img2El.src = img2.src;
    img2El.style.width = '100%';
    img2El.style.height = '100%';
    img2El.style.objectFit = 'fill';
    img2El.style.pointerEvents = 'none';
    img2El.style.display = 'block';
    
    wrapper2.appendChild(img2El);
    wrapper2.appendChild(createLabel('Image 2 (After)', false));
    container.appendChild(wrapper2);

    // Left / Top Overlaid Image Layer
    const overlay = document.createElement('div');
    overlay.style.position = 'absolute';
    overlay.style.top = '0';
    overlay.style.left = '0';
    overlay.style.width = '50%';
    overlay.style.height = '100%';
    overlay.style.overflow = 'hidden';

    const img1El = new Image();
    img1El.src = originalImg.src;
    img1El.style.position = 'absolute';
    img1El.style.top = '0';
    img1El.style.left = '0';
    img1El.style.height = '100%';
    img1El.style.objectFit = 'fill';
    img1El.style.pointerEvents = 'none';
    img1El.style.display = 'block';

    overlay.appendChild(img1El);
    overlay.appendChild(createLabel('Image 1 (Before)', true));
    container.appendChild(overlay);

    // Draggable Slider Handle
    const handle = document.createElement('div');
    handle.style.position = 'absolute';
    handle.style.top = '0';
    handle.style.bottom = '0';
    handle.style.left = '50%';
    handle.style.width = '4px';
    handle.style.backgroundColor = '#fff';
    handle.style.cursor = 'ew-resize';
    handle.style.transform = 'translateX(-50%)';
    handle.style.zIndex = '10';
    handle.style.boxShadow = '0 0 4px rgba(0,0,0,0.4)';

    const handleButton = document.createElement('div');
    handleButton.style.position = 'absolute';
    handleButton.style.top = '50%';
    handleButton.style.left = '50%';
    handleButton.style.transform = 'translate(-50%, -50%)';
    handleButton.style.width = '32px';
    handleButton.style.height = '32px';
    handleButton.style.backgroundColor = '#fff';
    handleButton.style.borderRadius = '50%';
    handleButton.style.display = 'flex';
    handleButton.style.alignItems = 'center';
    handleButton.style.justifyContent = 'center';
    handleButton.style.boxShadow = '0 2px 6px rgba(0,0,0,0.3)';
    
    handleButton.innerHTML = `<div style="display:flex; gap: 4px;">
        <div style="width: 0; height: 0; border-top: 5px solid transparent; border-bottom: 5px solid transparent; border-right: 6px solid #555;"></div>
        <div style="width: 0; height: 0; border-top: 5px solid transparent; border-bottom: 5px solid transparent; border-left: 6px solid #555;"></div>
    </div>`;

    handle.appendChild(handleButton);
    container.appendChild(handle);

    // Sync width of overlay's image dynamically
    const syncWidth = () => {
         img1El.style.width = container.clientWidth + 'px';
    };

    if (window.ResizeObserver) {
        const ro = new ResizeObserver(syncWidth);
        ro.observe(container);
    } else {
        syncWidth();
        window.addEventListener('resize', syncWidth);
    }

    // Interaction Events
    let isDragging = false;
    const moveHandler = (e) => {
        if (!isDragging) return;
        
        if (e.cancelable && e.type === 'touchmove') e.preventDefault(); // Stop mobile scroll while sliding
        
        const rect = container.getBoundingClientRect();
        let clientX = e.clientX;
        if (e.touches && e.touches.length > 0) {
            clientX = e.touches[0].clientX;
        }
        if (clientX === undefined) return;

        let xPos = clientX - rect.left;
        xPos = Math.max(0, Math.min(xPos, rect.width));
        
        const percent = (xPos / rect.width) * 100;
        overlay.style.width = `${percent}%`;
        handle.style.left = `${percent}%`;
    };

    const stopDrag = () => isDragging = false;
    const startDrag = (e) => {
        isDragging = true;
        moveHandler(e);
    };

    container.addEventListener('mousedown', startDrag);
    container.addEventListener('touchstart', startDrag, { passive: true });
    
    window.addEventListener('mousemove', moveHandler);
    window.addEventListener('touchmove', moveHandler, { passive: false });
    
    window.addEventListener('mouseup', stopDrag);
    window.addEventListener('touchend', stopDrag);

    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 Two Photo Comparison Search Tool allows you to visually analyze the differences between two images using three distinct viewing modes. You can use an interactive before/after slider to manually sweep through changes, a side-by-side mode for direct comparison, or a visual difference mode that highlights modified pixels in red against a grayscale background. This tool is ideal for real-world applications such as photo editing verification, spotting changes in architectural renders, comparing medical imaging, or reviewing quality control results in manufacturing.

Leave a Reply

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

Other Image Tools:

Image Name Official Tool

Video Audio Track Language Dubbing Translator Tool

Multi Language HiFi VHSRip and DVDRip Audio Player

Image To Stylized Character Hero Generator

AI Image Idea Generator

Image Topic Search and Mediateka Creator Tool

Image Based Advanced Movie Identifier Search Tool

Image Movie Identifier Advanced Search Tool

Image Aspect Ratio Calculator By Numbers

SEO Topic Search Image Finder Tool

VHSRip and DVDRip Audio Track and Language Translator Tool

Video Audio Track and Language Dub Text Translator Downloader

HIFI VHSRip Mono Effects Image Applier

Image To Online Website Address Converter

YouTube Video Title Renamer Based on URL Tool

Image URL Web Interface Tool

Movie Languages and Auto Dubbing Translation Tool

Video Platform Metadata and DVDRip Text Downloader Tool

YouTube Video Multilingual Auto Dubbing Tool

Video Platform Language Dubbing Text Downloader and Translator Tool

HIFI VHSRip Video Metadata and Subtitle Downloader

Video and Audio Metadata Text Extractor Tool

Search and Download TV Video Platform Audio Dubbing Text Translator

Video Platform Audio Track and Language Dubbing Text Downloader

Video Platform Audio Track Language Dub Translator and Downloader

Video Platform Audio Track Language Translator and Downloader

Video Platform Audio Track DVDRip Player Name Language Extractor

Image To Website Interface Address Converter

Image Name And Topic Identifier Tool

Image Based Software Program Identifier

Audio Lyric MP3 Player and Downloader

Song Lyric MP3 Player and Downloader

AI Powered Image Tool Idea Generator

Image Movie Studio Company Identifier

No Image Result

Image SEO Name Finder Tool

See All →