Please bookmark this page to avoid losing your image tool!

Image Crossover Mashup Generator

(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, mashupStyle = "checkerboard", mashupComplexity = 8, secondaryImageUrl = "", blendMode = "source-over") {
    const width = originalImg.width;
    const height = originalImg.height;
    
    // Main canvas
    const canvas = document.createElement("canvas");
    canvas.width = width;
    canvas.height = height;
    const ctx = canvas.getContext("2d");

    // Load secondary image for the mashup or generate a unique variation of the original
    let img2 = null;
    if (typeof secondaryImageUrl === "string" && secondaryImageUrl.trim() !== "") {
        img2 = new Image();
        img2.crossOrigin = "Anonymous";
        const loaded = await new Promise((resolve) => {
            img2.onload = () => resolve(true);
            img2.onerror = () => resolve(false);
            img2.src = secondaryImageUrl.trim();
        });
        if (!loaded || !img2.width || !img2.height) {
            img2 = null; // Fallback entirely if the image failed to load
        }
    }

    if (!img2) {
        // Fallback: Create a cool procedural crossover candidate
        // A flipped, hue-rotated, and saturated version of the original image
        const tCanv = document.createElement("canvas");
        tCanv.width = width;
        tCanv.height = height;
        const tCanvCtx = tCanv.getContext("2d");
        tCanvCtx.translate(width, 0);
        tCanvCtx.scale(-1, 1); // Flip horizontally
        tCanvCtx.filter = "hue-rotate(180deg) saturate(200%)";
        tCanvCtx.drawImage(originalImg, 0, 0, width, height);
        img2 = tCanv;
    }

    // Draw the base layer (original image)
    ctx.drawImage(originalImg, 0, 0, width, height);

    // Prepare temp layer for secondary image to ensure it strictly respects the canvas dimensions
    const tempLayer = document.createElement("canvas");
    tempLayer.width = width;
    tempLayer.height = height;
    const tempCtx = tempLayer.getContext("2d");
    tempCtx.drawImage(img2, 0, 0, width, height);

    // Filter valid blending composite modes
    const validBlendModes = [
        "source-over", "source-in", "source-out", "source-atop",
        "destination-over", "destination-in", "destination-out", "destination-atop",
        "lighter", "copy", "xor", "multiply", "screen", "overlay", "darken",
        "lighten", "color-dodge", "color-burn", "hard-light", "soft-light",
        "difference", "exclusion", "hue", "saturation", "color", "luminosity"
    ];
    const targetBlendMode = validBlendModes.includes(blendMode) ? blendMode : "source-over";
    ctx.globalCompositeOperation = targetBlendMode;

    // Parse options
    const mStyle = (typeof mashupStyle === "string" ? mashupStyle : "checkerboard").toLowerCase();
    let subs = parseInt(mashupComplexity);
    if (isNaN(subs) || subs < 2) subs = 8;
    if (subs > 100) subs = 100; // reasonable complexity cap

    // Helper to draw clipped regions of the second image to create the mashup
    const applyClip = (clipFn) => {
        ctx.save();
        ctx.beginPath();
        clipFn();
        ctx.clip();
        ctx.drawImage(tempLayer, 0, 0, width, height);
        ctx.restore();
    };

    // Apply selected mashup style
    if (mStyle === "double-exposure" || mStyle === "blend") {
        if (targetBlendMode === "source-over") {
            ctx.globalCompositeOperation = "screen"; // default to screen for natural double-exposure
        }
        ctx.drawImage(tempLayer, 0, 0, width, height);
    } else if (mStyle === "horizontal-stripes") {
        applyClip(() => {
            const cellH = height / subs;
            for (let y = 0; y < subs; y++) {
                if (y % 2 === 1) ctx.rect(0, y * cellH, width, cellH);
            }
        });
    } else if (mStyle === "vertical-stripes") {
        applyClip(() => {
            const cellW = width / subs;
            for (let x = 0; x < subs; x++) {
                if (x % 2 === 1) ctx.rect(x * cellW, 0, cellW, height);
            }
        });
    } else if (mStyle === "diagonal-split") {
        applyClip(() => {
            ctx.moveTo(0, height);
            ctx.lineTo(width, 0);
            ctx.lineTo(width, height);
            ctx.closePath();
        });
    } else if (mStyle === "triangles") {
        applyClip(() => {
            const cellW = width / subs;
            const cellH = height / subs;
            for (let y = 0; y < subs; y++) {
                for (let x = 0; x < subs; x++) {
                    if ((x + y) % 2 === 0) {
                        ctx.moveTo(x * cellW, y * cellH);
                        ctx.lineTo((x + 1) * cellW, y * cellH);
                        ctx.lineTo(x * cellW, (y + 1) * cellH);
                    } else {
                        ctx.moveTo((x + 1) * cellW, (y + 1) * cellH);
                        ctx.lineTo((x + 1) * cellW, y * cellH);
                        ctx.lineTo(x * cellW, (y + 1) * cellH);
                    }
                    ctx.closePath();
                }
            }
        });
    } else if (mStyle === "circle-center") {
        applyClip(() => {
            ctx.arc(width / 2, height / 2, Math.min(width, height) * 0.4, 0, Math.PI * 2);
        });
    } else if (mStyle === "glitch-slices") {
        applyClip(() => {
            // Apply randomized horizontal sliced intersections
            for (let i = 0; i < subs * 2; i++) {
                const y = (height / (subs * 2)) * i + (Math.random() * (height / subs) - (height / (subs * 2)));
                const sliceH = Math.random() * (height / subs);
                ctx.rect(0, y, width, sliceH);
            }
        });
    } else {
        // Fallback or "checkerboard" default
        applyClip(() => {
            const cellW = width / subs;
            const cellH = height / subs;
            for (let y = 0; y < subs; y++) {
                for (let x = 0; x < subs; x++) {
                    // Check for odd intersected blocks to create pattern
                    if ((x + y) % 2 === 1) {
                        ctx.rect(x * cellW, y * cellH, cellW, cellH);
                    }
                }
            }
        });
    }

    ctx.globalCompositeOperation = "source-over"; // Cleanup 
    return canvas;
}

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 Crossover Mashup Generator is a creative tool designed to merge two images into a single composite using various artistic patterns and blending techniques. Users can combine a primary image with a secondary image—or generate a stylized variation of the original—using styles such as double exposure, checkerboard, stripes, triangles, diagonal splits, and glitch-slice effects. The tool also supports a wide range of professional blending modes to control how the images interact. This tool is ideal for digital artists, social media creators, and designers looking to produce unique textures, abstract backgrounds, or experimental visual content.

Leave a Reply

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