Please bookmark this page to avoid losing your image tool!

Photo Comparison 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, comparisonImgSrc, orientation = 'horizontal', initialPosition = 50, label1 = 'Before', label2 = 'After') {
    /**
     * Loads an image from a URL or data URI.
     * @param {string} src The source of the image.
     * @returns {Promise<HTMLImageElement>} A promise that resolves with the loaded image element.
     */
    const loadImage = (src) => {
        return new Promise((resolve, reject) => {
            const img = new Image();
            // Allow loading images from other origins for use in canvas etc.
            img.crossOrigin = "Anonymous";
            img.onload = () => resolve(img);
            img.onerror = (err) => reject(new Error(`Failed to load comparison image: ${err}`));
            img.src = src;
        });
    };

    if (!(originalImg instanceof HTMLImageElement)) {
        console.error("The first parameter 'originalImg' must be an HTMLImageElement.");
        const errorDiv = document.createElement('div');
        errorDiv.textContent = 'Error: Invalid original image provided.';
        return errorDiv;
    }

    if (typeof comparisonImgSrc !== 'string' || comparisonImgSrc.trim() === '') {
        console.error("The second parameter 'comparisonImgSrc' must be a valid image URL or data URI string.");
        const errorDiv = document.createElement('div');
        errorDiv.textContent = 'Error: Invalid comparison image source provided.';
        return errorDiv;
    }

    try {
        const comparisonImg = await loadImage(comparisonImgSrc);

        const width = originalImg.naturalWidth;
        const height = originalImg.naturalHeight;

        // Create the main container
        const container = document.createElement('div');
        Object.assign(container.style, {
            position: 'relative',
            width: `${width}px`,
            height: `${height}px`,
            overflow: 'hidden',
            cursor: orientation === 'horizontal' ? 'ew-resize' : 'ns-resize',
            userSelect: 'none',
            fontFamily: 'Helvetica, Arial, sans-serif'
        });

        // Create the original image element (bottom layer)
        const originalImgElement = originalImg.cloneNode();
        Object.assign(originalImgElement.style, {
            position: 'absolute',
            top: '0',
            left: '0',
            width: '100%',
            height: '100%',
            pointerEvents: 'none'
        });
        container.appendChild(originalImgElement);

        // Create a wrapper for the comparison image (for clipping)
        const comparisonWrapper = document.createElement('div');
        Object.assign(comparisonWrapper.style, {
            position: 'absolute',
            top: '0',
            left: '0',
            width: '100%',
            height: '100%',
            overflow: 'hidden',
            pointerEvents: 'none'
        });
        container.appendChild(comparisonWrapper);

        // Crate the comparison image element (top layer)
        const comparisonImgElement = comparisonImg.cloneNode();
        Object.assign(comparisonImgElement.style, {
            position: 'absolute',
            top: '0',
            left: '0',
            width: `${width}px`,
            height: `${height}px`
        });
        comparisonWrapper.appendChild(comparisonImgElement);
        
        // Create the slider line and handle
        const slider = document.createElement('div');
        slider.style.position = 'absolute';
        slider.style.zIndex = '10';
        
        const sliderLine = document.createElement('div');
        sliderLine.style.position = 'absolute';
        sliderLine.style.backgroundColor = 'white';
        sliderLine.style.boxShadow = '0px 0px 3px rgba(0, 0, 0, 0.7)';
        slider.appendChild(sliderLine);

        const sliderHandle = document.createElement('div');
        Object.assign(sliderHandle.style, {
            position: 'absolute',
            width: '44px',
            height: '44px',
            borderRadius: '50%',
            border: '2px solid white',
            backgroundColor: 'rgba(0, 0, 0, 0.3)',
            boxShadow: '0px 0px 5px rgba(0, 0, 0, 0.7)',
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'space-around',
            boxSizing: 'border-box'
        });
        
        // Add SVG arrows to handle (self-contained)
        const arrowSVG = `<svg viewBox="0 0 24 24" width="20" height="20" stroke="white" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"></polyline></svg>`;
        sliderHandle.innerHTML = `${arrowSVG}${arrowSVG.replace('15 18 9 12 15 6', '9 18 15 12 9 6')}`;
        slider.appendChild(sliderHandle);

        // Style slider based on orientation
        if (orientation === 'horizontal') {
            Object.assign(slider.style, { top: '0', height: '100%', width: '1px', marginLeft: '-0.5px' });
            Object.assign(sliderLine.style, { width: '1px', height: '100%', left: '0' });
            Object.assign(sliderHandle.style, { top: '50%', left: '50%', transform: 'translate(-50%, -50%)' });
        } else { // vertical
            Object.assign(slider.style, { left: '0', width: '100%', height: '1px', marginTop: '-0.5px' });
            Object.assign(sliderLine.style, { height: '1px', width: '100%', top: '0' });
            Object.assign(sliderHandle.style, { left: '50%', top: '50%', transform: 'translate(-50%, -50%) rotate(90deg)' });
        }

        container.appendChild(slider);
        
        // Add labels
        const createLabel = (text) => {
            const labelEl = document.createElement('div');
            labelEl.textContent = text;
            Object.assign(labelEl.style, {
                position: 'absolute',
                top: '1rem',
                padding: '0.5rem 1rem',
                backgroundColor: 'rgba(0, 0, 0, 0.6)',
                color: 'white',
                borderRadius: '4px',
                pointerEvents: 'none',
                zIndex: '5'
            });
            return labelEl;
        };

        const beforeLabelEl = createLabel(label1);
        beforeLabelEl.style.left = '1rem';
        container.appendChild(beforeLabelEl);

        const afterLabelEl = createLabel(label2);
        afterLabelEl.style.right = '1rem';
        comparisonWrapper.appendChild(afterLabelEl); // Add to wrapper so it gets clipped


        // --- Interaction Logic ---
        let isDragging = false;
        
        const updateSliderPosition = (value) => {
             const clampedValue = Math.max(0, Math.min(value, 100));
             if (orientation === 'horizontal') {
                slider.style.left = `${clampedValue}%`;
                comparisonWrapper.style.width = `${clampedValue}%`;
             } else {
                slider.style.top = `${clampedValue}%`;
                comparisonWrapper.style.height = `${clampedValue}%`;
             }
        };

        const getPositionFromEvent = (e) => {
            const rect = container.getBoundingClientRect();
            const clientX = e.clientX || e.touches[0].clientX;
            const clientY = e.clientY || e.touches[0].clientY;
            const x = clientX - rect.left;
            const y = clientY - rect.top;
            
            if (orientation === 'horizontal') {
                return (x / width) * 100;
            } else {
                return (y / height) * 100;
            }
        };

        const startSlide = (e) => {
            e.preventDefault();
            isDragging = true;
            window.addEventListener('mousemove', moveSlide, { passive: true });
            window.addEventListener('touchmove', moveSlide, { passive: true });
            window.addEventListener('mouseup', stopSlide);
            window.addEventListener('touchend', stopSlide);
        };

        const moveSlide = (e) => {
            if (!isDragging) return;
            const pos = getPositionFromEvent(e);
            updateSliderPosition(pos);
        };
        
        const stopSlide = () => {
             isDragging = false;
             window.removeEventListener('mousemove', moveSlide);
             window.removeEventListener('touchmove', moveSlide);
             window.removeEventListener('mouseup', stopSlide);
             window.removeEventListener('touchend', stopSlide);
        };

        container.addEventListener('mousedown', startSlide);
        container.addEventListener('touchstart', startSlide, { passive: false });

        // Set initial position
        updateSliderPosition(initialPosition);

        return container;

    } catch (error) {
        console.error(error);
        const errorDiv = document.createElement('div');
        errorDiv.textContent = `Error: ${error.message}`;
        return errorDiv;
    }
}

Free Image Tool Creator

Can't find the image tool you're looking for?
Create one based on your own needs now!

Description

The Photo Comparison Tool allows users to visually compare two images side by side, with an interactive slider that enables them to reveal differences between the original and the comparison image. Users can adjust the slider horizontally or vertically, depending on their preference, to see how the images differ in detail. This tool is beneficial for applications such as evaluating before-and-after photos, analyzing modifications in design, showcasing product changes, or comparing visual content for quality assessments.

Leave a Reply

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