Please bookmark this page to avoid losing your image tool!

Image Comparison Tool For Channel Vs YouTube

(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.
/**
 * Creates an interactive image comparison slider element.
 *
 * @param {HTMLImageElement} originalImg The base image element for the comparison.
 * @param {string} secondImgSrc A URL or Data URI for the second image to compare against.
 * @param {string} orientation The slider orientation, either 'horizontal' or 'vertical'.
 * @param {string} label1 The text label for the base image (left/top side).
 * @param {string} label2 The text label for the second image (right/bottom side).
 * @param {number} initialPosition The initial position of the slider as a percentage (0-100).
 * @returns {Promise<HTMLDivElement>} A promise that resolves to the container DIV element for the comparison tool.
 */
async function processImage(originalImg, secondImgSrc = '', orientation = 'horizontal', label1 = 'Channel', label2 = 'YouTube', initialPosition = 50) {
    // --- 1. Validate inputs and prepare images ---
    if (!originalImg || !(originalImg instanceof HTMLImageElement) || !originalImg.width) {
        const errorDiv = document.createElement('div');
        errorDiv.textContent = 'Error: The first parameter must be a loaded HTMLImageElement.';
        return errorDiv;
    }

    // If no second image is provided, create a placeholder.
    if (!secondImgSrc) {
        const canvas = document.createElement('canvas');
        canvas.width = originalImg.width;
        canvas.height = originalImg.height;
        const ctx = canvas.getContext('2d');
        ctx.fillStyle = '#888';
        ctx.fillRect(0, 0, canvas.width, canvas.height);
        ctx.fillStyle = 'white';
        ctx.font = `bold ${Math.min(24, originalImg.width / 15)}px sans-serif`;
        ctx.textAlign = 'center';
        ctx.textBaseline = 'middle';
        ctx.fillText('Second Image', canvas.width / 2, canvas.height / 2);
        secondImgSrc = canvas.toDataURL();
    }

    // Load the second image asynchronously.
    const secondImg = await new Promise((resolve) => {
        const img = new Image();
        img.crossOrigin = "Anonymous";
        img.onload = () => resolve(img);
        img.onerror = () => {
            // Create a fallback error image if loading fails.
            const canvas = document.createElement('canvas');
            canvas.width = originalImg.width;
            canvas.height = originalImg.height;
            const ctx = canvas.getContext('2d');
            ctx.fillStyle = '#ffdddd';
            ctx.fillRect(0, 0, canvas.width, canvas.height);
            ctx.fillStyle = 'red';
            ctx.font = `bold ${Math.min(24, originalImg.width / 15)}px sans-serif`;
            ctx.textAlign = 'center';
            ctx.textBaseline = 'middle';
            ctx.fillText('Error loading image', canvas.width / 2, canvas.height / 2);
            const errorImg = new Image();
            errorImg.src = canvas.toDataURL();
            errorImg.onload = () => resolve(errorImg);
        };
        img.src = secondImgSrc;
    });

    const width = originalImg.width;
    const height = originalImg.height;

    // --- 2. Create the HTML structure ---
    const container = document.createElement('div');
    const firstImageWrapper = document.createElement('div');
    const secondImageWrapper = document.createElement('div');
    const slider = document.createElement('div');
    const sliderHandle = document.createElement('div');
    const firstLabel = document.createElement('div');
    const secondLabel = document.createElement('div');
    const arrowLeft = document.createElement('div');
    const arrowRight = document.createElement('div');

    const firstImgClone = originalImg.cloneNode(true);
    firstImgClone.style.width = '100%';
    firstImgClone.style.height = '100%';
    secondImg.style.width = `${width}px`;
    secondImg.style.height = `${height}px`;
    secondImg.style.maxWidth = 'none';

    sliderHandle.appendChild(arrowLeft);
    sliderHandle.appendChild(arrowRight);
    slider.appendChild(sliderHandle);
    firstImageWrapper.appendChild(firstImgClone);
    secondImageWrapper.appendChild(secondImg);
    container.appendChild(firstImageWrapper);
    container.appendChild(secondImageWrapper);
    container.appendChild(firstLabel);
    container.appendChild(secondLabel);
    container.appendChild(slider);

    // --- 3. Style the elements ---
    const isHorizontal = orientation.toLowerCase() !== 'vertical';

    Object.assign(container.style, {
        position: 'relative',
        width: `${width}px`,
        height: `${height}px`,
        overflow: 'hidden',
        cursor: 'default',
        userSelect: 'none',
        fontFamily: 'Arial, sans-serif'
    });

    Object.assign(firstImageWrapper.style, {
        position: 'absolute', top: '0', left: '0', width: '100%', height: '100%', zIndex: '1'
    });

    Object.assign(secondImageWrapper.style, {
        position: 'absolute', top: '0', left: '0', width: '100%', height: '100%', overflow: 'hidden', zIndex: '2'
    });

    Object.assign(slider.style, {
        position: 'absolute',
        top: '0',
        left: '0',
        width: isHorizontal ? '3px' : '100%',
        height: isHorizontal ? '100%' : '3px',
        background: 'rgba(255, 255, 255, 0.9)',
        boxShadow: '0 0 5px rgba(0, 0, 0, 0.5)',
        cursor: isHorizontal ? 'ew-resize' : 'ns-resize',
        zIndex: '10',
        transform: isHorizontal ? 'translateX(-50%)' : 'translateY(-50%)'
    });

    Object.assign(sliderHandle.style, {
        position: 'absolute',
        background: 'rgba(255, 255, 255, 0.9)',
        border: '3px solid white',
        borderRadius: '50%',
        boxShadow: '0 0 10px rgba(0,0,0,0.5)',
        width: '40px',
        height: '40px',
        top: '50%',
        left: '50%',
        transform: 'translate(-50%, -50%)',
    });

    const arrowBaseStyle = {
        position: 'absolute',
        top: '50%',
        left: '50%',
        width: '10px',
        height: '10px',
        borderTop: '3px solid #333',
        borderRight: '3px solid #333'
    };

    Object.assign(arrowLeft.style, arrowBaseStyle);
    Object.assign(arrowRight.style, arrowBaseStyle);

    if (isHorizontal) {
        arrowLeft.style.transform = 'translate(-12px, -50%) rotate(-135deg)';
        arrowRight.style.transform = 'translate(2px, -50%) rotate(45deg)';
    } else {
        arrowLeft.style.transform = 'translate(-50%, -12px) rotate(225deg)'; // up arrow
        arrowRight.style.transform = 'translate(-50%, 2px) rotate(45deg)'; // down arrow
    }

    const labelBaseStyle = {
        position: 'absolute',
        background: 'rgba(0,0,0,0.7)',
        color: 'white',
        padding: '8px 12px',
        borderRadius: '5px',
        pointerEvents: 'none',
        zIndex: '5',
        fontSize: '14px',
        fontWeight: 'bold',
        textTransform: 'uppercase'
    };
    Object.assign(firstLabel.style, labelBaseStyle);
    Object.assign(secondLabel.style, labelBaseStyle);
    firstLabel.textContent = label1;
    secondLabel.textContent = label2;
    firstLabel.style.top = '10px';
    firstLabel.style.left = '10px';
    secondLabel.style.top = '10px';
    secondLabel.style.right = '10px';


    // --- 4. Interaction Logic ---
    const moveSlider = (pos) => {
        if (isHorizontal) {
            const percentage = Math.max(0, Math.min(100, (pos / width) * 100));
            slider.style.left = `${percentage}%`;
            secondImageWrapper.style.width = `${percentage}%`;
        } else {
            const percentage = Math.max(0, Math.min(100, (pos / height) * 100));
            slider.style.top = `${percentage}%`;
            secondImageWrapper.style.height = `${percentage}%`;
        }
    };

    const doDrag = (e) => {
        e.preventDefault();
        const rect = container.getBoundingClientRect();
        if (isHorizontal) {
            const x = (e.touches ? e.touches[0].clientX : e.clientX) - rect.left;
            moveSlider(x);
        } else {
            const y = (e.touches ? e.touches[0].clientY : e.clientY) - rect.top;
            moveSlider(y);
        }
    };
    
    const stopDrag = () => {
        window.removeEventListener('mousemove', doDrag);
        window.removeEventListener('touchmove', doDrag);
        window.removeEventListener('mouseup', stopDrag);
        window.removeEventListener('touchend', stopDrag);
    };
    
    const startDrag = (e) => {
        e.preventDefault();
        window.addEventListener('mousemove', doDrag);
        window.addEventListener('touchmove', doDrag);
        window.addEventListener('mouseup', stopDrag);
        window.addEventListener('touchend', stopDrag);
    };

    slider.addEventListener('mousedown', startDrag);
    slider.addEventListener('touchstart', startDrag, { passive: false });

    // --- 5. Set initial position and return ---
    if (isHorizontal) {
        moveSlider(width * (initialPosition / 100));
    } else {
        moveSlider(height * (initialPosition / 100));
    }

    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 Comparison Tool for Channel Vs YouTube allows users to interactively compare two images side by side, using a slider that adjusts the visibility of each image. Users can upload an original image and a second image to see differences visually, which is particularly useful for content creators reviewing changes between two versions of artwork or screenshots. The tool supports both horizontal and vertical slider orientations and provides flexible initial positioning of the slider for tailored comparisons.

Leave a Reply

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