Please bookmark this page to avoid losing your image tool!

Image Torn Paper Collage Filter Effect 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,
    numPieces = 5,
    maxOffset = 20,
    maxRotation = 15,
    edgeRoughness = 10,
    subSegmentsPerEdge = 15,
    shadowColor = 'rgba(0,0,0,0.3)',
    shadowBlur = 5,
    shadowOffsetX = 3,
    shadowOffsetY = 3,
    backgroundColor = 'transparent'
) {

    // Helper for robust number parsing
    function parseNumericParam(value, defaultValue) {
        const num = Number(value);
        return isNaN(num) ? defaultValue : num;
    }

    // Sanitize parameters
    numPieces = Math.max(1, parseNumericParam(numPieces, 5));
    maxOffset = parseNumericParam(maxOffset, 20);
    maxRotation = parseNumericParam(maxRotation, 15);
    edgeRoughness = Math.max(0, parseNumericParam(edgeRoughness, 10)); // Roughness cannot be negative
    subSegmentsPerEdge = Math.max(1, parseNumericParam(subSegmentsPerEdge, 15)); // At least 1 segment
    shadowBlur = Math.max(0, parseNumericParam(shadowBlur, 5));
    shadowOffsetX = parseNumericParam(shadowOffsetX, 3);
    shadowOffsetY = parseNumericParam(shadowOffsetY, 3);
    shadowColor = String(shadowColor);
    backgroundColor = String(backgroundColor);

    const canvas = document.createElement('canvas');
    canvas.width = originalImg.width;
    canvas.height = originalImg.height;
    const ctx = canvas.getContext('2d');

    if (backgroundColor !== 'transparent' && backgroundColor !== '') {
        ctx.fillStyle = backgroundColor;
        ctx.fillRect(0, 0, canvas.width, canvas.height);
    }

    // Helper function to draw a jagged line segment.
    // 'fromX, fromY' are the conceptual start of the straight segment.
    // 'toX, toY' are the conceptual end of the straight segment.
    // The actual drawing uses ctx.lineTo and extends the current path.
    function drawJaggedLineSegment(ctx, fromX, fromY, toX, toY, roughness, numSubSegments) {
        const dx = toX - fromX;
        const dy = toY - fromY;
        const len = Math.sqrt(dx * dx + dy * dy);

        if (len < 0.001 || roughness === 0) {
            ctx.lineTo(toX, toY);
            return;
        }

        // Normalized perpendicular vector to the main segment direction
        const perpX = -dy / len;
        const perpY = dx / len;

        for (let i = 1; i < numSubSegments; i++) { // Jitter intermediate points
            const t = i / numSubSegments; // Progress along the main segment (0 to 1)

            // Nominal point on the straight line segment
            const nominalX = fromX + dx * t;
            const nominalY = fromY + dy * t;

            // Random jitter amount, perpendicular to the segment
            const jitterAmount = (Math.random() * 2 - 1) * roughness;

            ctx.lineTo(nominalX + perpX * jitterAmount, nominalY + perpY * jitterAmount);
        }
        ctx.lineTo(toX, toY); // Ensure the segment ends at the target point
    }


    for (let i = 0; i < numPieces; i++) {
        ctx.save();

        // 1. Determine source rectangle from originalImg (part of the image to use for this piece)
        //    Aim for pieces that are roughly proportional to image dimensions.
        const basePieceWidth = originalImg.width / Math.max(1, Math.sqrt(numPieces) * 0.9); // Allow some overlap
        const basePieceHeight = originalImg.height / Math.max(1, Math.sqrt(numPieces) * 0.9);

        // Add random variation to piece size and aspect ratio
        const widthVariation = 0.65 + Math.random() * 0.7; // Random factor between 0.65 and 1.35
        const heightVariation = 0.65 + Math.random() * 0.7;

        let srcW = basePieceWidth * widthVariation;
        let srcH = basePieceHeight * heightVariation;

        // Clamp piece dimensions to be within reasonable bounds of the original image size
        srcW = Math.max(originalImg.width * 0.10, Math.min(srcW, originalImg.width * 0.95));
        srcH = Math.max(originalImg.height * 0.10, Math.min(srcH, originalImg.height * 0.95));

        const srcX = Math.max(0, Math.random() * (originalImg.width - srcW));
        const srcY = Math.max(0, Math.random() * (originalImg.height - srcH));

        // 2. Determine piece position and rotation on the destination canvas
        //    Try to place pieces somewhat centrally, not all clustered at edges.
        const targetCenterX = (0.15 + Math.random() * 0.7) * canvas.width;
        const targetCenterY = (0.15 + Math.random() * 0.7) * canvas.height;

        const pieceRotation = (Math.random() - 0.5) * 2 * maxRotation; // In degrees
        const pieceOffsetX = (Math.random() - 0.5) * 2 * maxOffset;
        const pieceOffsetY = (Math.random() - 0.5) * 2 * maxOffset;

        // Apply transformations: translate to piece's target center, then rotate
        ctx.translate(targetCenterX + pieceOffsetX, targetCenterY + pieceOffsetY);
        ctx.rotate(pieceRotation * Math.PI / 180);
        // All subsequent drawing operations for this piece are relative to its rotated center (0,0).

        // 3. Set shadow properties for the piece
        if (shadowBlur > 0 || shadowOffsetX !== 0 || shadowOffsetY !== 0) {
            ctx.shadowColor = shadowColor;
            ctx.shadowBlur = shadowBlur;
            ctx.shadowOffsetX = shadowOffsetX;
            ctx.shadowOffsetY = shadowOffsetY;
        }

        // 4. Create the torn path for clipping
        //    The path is defined around (0,0), which is the piece's center.
        const halfW = srcW / 2;
        const halfH = srcH / 2;

        // Max deviation for corners from a perfect rectangle, proportional to roughness
        const cornerPerturbAmount = edgeRoughness * 0.3;
        const randPerturb = () => (Math.random() - 0.5) * 2 * cornerPerturbAmount;

        // Define the four nominal corners of the piece, with slight perturbations
        const p_tl = { x: -halfW + randPerturb(), y: -halfH + randPerturb() }; // Top-left
        const p_tr = { x:  halfW + randPerturb(), y: -halfH + randPerturb() }; // Top-right
        const p_br = { x:  halfW + randPerturb(), y:  halfH + randPerturb() }; // Bottom-right
        const p_bl = { x: -halfW + randPerturb(), y:  halfH + randPerturb() }; // Bottom-left

        ctx.beginPath();
        ctx.moveTo(p_tl.x, p_tl.y);
        drawJaggedLineSegment(ctx, p_tl.x, p_tl.y, p_tr.x, p_tr.y, edgeRoughness, subSegmentsPerEdge); // Top edge
        drawJaggedLineSegment(ctx, p_tr.x, p_tr.y, p_br.x, p_br.y, edgeRoughness, subSegmentsPerEdge); // Right edge
        drawJaggedLineSegment(ctx, p_br.x, p_br.y, p_bl.x, p_bl.y, edgeRoughness, subSegmentsPerEdge); // Bottom edge
        drawJaggedLineSegment(ctx, p_bl.x, p_bl.y, p_tl.x, p_tl.y, edgeRoughness, subSegmentsPerEdge); // Left edge (to close)
        ctx.closePath();

        // 5. Apply clipping using the created path
        ctx.clip();

        // 6. Draw the corresponding part of the original image into the clipped, transformed area
        //    The image is drawn centered at (0,0) in the current transformed space.
        ctx.drawImage(originalImg, srcX, srcY, srcW, srcH, -halfW, -halfH, srcW, srcH);

        ctx.restore(); // Restore transform, shadow, and clipping state for the next piece
    }
    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 Torn Paper Collage Filter Effect Tool allows users to create visually appealing collages by simulating torn paper effects on images. This tool enables you to specify parameters such as the number of pieces to create, the maximum rotational variance, shadow effects, and edge roughness to customize the collage’s appearance. It is ideal for artistic projects, social media posts, or digital scrapbooking, adding a creative touch to photos by giving them a fragmented, artistic look reminiscent of collage art.

Leave a Reply

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