Please bookmark this page to avoid losing your image tool!

Ruined Paint With Broken Pieces Image Effect 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.
function processImage(originalImg, piecesDensityStr = "40", crackAmountStr = "5", peelProbabilityStr = "0.15", ruinLevelStr = "0.6") {
    // Parse constraint parameters
    const piecesDensity = Math.max(5, Math.min(parseFloat(piecesDensityStr) || 40, 150));
    const crackAmount = Math.max(0, Math.min(parseFloat(crackAmountStr) || 5, 20));
    const peelProb = Math.max(0, Math.min(parseFloat(peelProbabilityStr) || 0.15, 0.95));
    const ruinLvl = Math.max(0, Math.min(parseFloat(ruinLevelStr) || 0.6, 1));

    const w = originalImg.width;
    const h = originalImg.height;

    // Create main canvas
    const canvas = document.createElement('canvas');
    canvas.width = w;
    canvas.height = h;
    const ctx = canvas.getContext('2d');

    // 1. Generate a procedural rusty/dark background for peeled areas
    const patSize = 256;
    const pCan = document.createElement('canvas');
    pCan.width = patSize;
    pCan.height = patSize;
    const pCtx = pCan.getContext('2d');
    const pData = pCtx.createImageData(patSize, patSize);

    for (let i = 0; i < pData.data.length; i += 4) {
        const val = Math.random() * 255;
        pData.data[i] = val * 0.6 + 40;     // R
        pData.data[i + 1] = val * 0.3 + 20; // G
        pData.data[i + 2] = val * 0.2 + 10; // B
        pData.data[i + 3] = 255;            // A
    }
    pCtx.putImageData(pData, 0, 0);

    // Add darker grunge spots to background
    for (let i = 0; i < 40; i++) {
        pCtx.fillStyle = `rgba(0,0,0,${Math.random() * 0.6})`;
        pCtx.beginPath();
        pCtx.arc(Math.random() * patSize, Math.random() * patSize, Math.random() * 25, 0, Math.PI * 2);
        pCtx.fill();
    }
    
    let bgPattern = ctx.createPattern(pCan, 'repeat');
    ctx.fillStyle = bgPattern;
    ctx.fillRect(0, 0, w, h);

    // 2. Compute a randomized shattered grid
    const longestEdge = Math.max(w, h);
    const cols = Math.max(2, Math.round(piecesDensity * (w / longestEdge)));
    const rows = Math.max(2, Math.round(piecesDensity * (h / longestEdge)));
    const cellW = w / cols;
    const cellH = h / rows;

    let points = [];
    for (let y = 0; y <= rows; y++) {
        let row = [];
        for (let x = 0; x <= cols; x++) {
            let px = x * cellW;
            let py = y * cellH;
            
            // Perturb interior points, keeping edges constrained
            if (x > 0 && x < cols) {
                px += (Math.random() - 0.5) * cellW * 0.8;
            }
            if (y > 0 && y < rows) {
                py += (Math.random() - 0.5) * cellH * 0.8;
            }
            row.push({ x: px, y: py });
        }
        points.push(row);
    }

    // Connect points into triangles for the "broken pieces"
    let triangles = [];
    for (let y = 0; y < rows; y++) {
        for (let x = 0; x < cols; x++) {
            let pTopLeft = points[y][x];
            let pTopRight = points[y][x + 1];
            let pBotLeft = points[y + 1][x];
            let pBotRight = points[y + 1][x + 1];

            // Randomize diagonal split to make it look organically shattered
            if (Math.random() > 0.5) {
                triangles.push([pTopLeft, pTopRight, pBotRight]);
                triangles.push([pTopLeft, pBotRight, pBotLeft]);
            } else {
                triangles.push([pTopLeft, pTopRight, pBotLeft]);
                triangles.push([pTopRight, pBotRight, pBotLeft]);
            }
        }
    }

    // 3. Draw broken pieces randomly detached and rotated
    const maxDisplacement = (w / 100) * (crackAmount / 5);

    for (let tri of triangles) {
        if (Math.random() < peelProb) continue; // Chance of piece peeling off completely

        const [p1, p2, p3] = tri;

        let minX = Math.floor(Math.min(p1.x, p2.x, p3.x));
        let minY = Math.floor(Math.min(p1.y, p2.y, p3.y));
        let maxX = Math.ceil(Math.max(p1.x, p2.x, p3.x));
        let maxY = Math.ceil(Math.max(p1.y, p2.y, p3.y));

        // Adding 2px padding to avoid precision artifacts on bounds
        minX = Math.max(0, minX - 2);
        minY = Math.max(0, minY - 2);
        maxX = Math.min(w, maxX + 2);
        maxY = Math.min(h, maxY + 2);
        
        const tw = maxX - minX;
        const th = maxY - minY;
        if (tw <= 0 || th <= 0) continue;

        // Centroid mapping
        const cx = (p1.x + p2.x + p3.x) / 3;
        const cy = (p1.y + p2.y + p3.y) / 3;

        ctx.save();

        // Calculate transformations to make pieces look disjointed/cracked
        const dx = (Math.random() - 0.5) * maxDisplacement;
        const dy = (Math.random() - 0.5) * maxDisplacement;
        const rot = (Math.random() - 0.5) * (crackAmount * 0.015);
        const scl = 1 - (crackAmount * 0.005) - (Math.random() * 0.01);

        // Apply transformations
        ctx.translate(cx + dx, cy + dy);
        ctx.rotate(rot);
        ctx.scale(scl, scl);
        ctx.translate(-cx, -cy);

        // Form clipping path for this shard
        ctx.beginPath();
        ctx.moveTo(p1.x, p1.y);
        ctx.lineTo(p2.x, p2.y);
        ctx.lineTo(p3.x, p3.y);
        ctx.closePath();
        ctx.clip();

        // Draw original image part
        ctx.drawImage(originalImg, minX, minY, tw, th, minX, minY, tw, th);

        // Shade overlay to simulate distinct tilted paint shards
        ctx.fillStyle = Math.random() > 0.5 ? 'rgba(255, 255, 255, 0.07)' : 'rgba(0, 0, 0, 0.07)';
        ctx.fill();

        // Outline each piece to trace the crack depth
        ctx.lineWidth = Math.max(1, w / 600);
        ctx.strokeStyle = 'rgba(0, 0, 0, 0.6)';
        ctx.stroke();

        ctx.restore();
    }

    // 4. Apply a global "ruined surface" dirt overlay
    if (ruinLvl > 0) {
        // Recycle pattern canvas for a grime map
        for (let i = 0; i < pData.data.length; i += 4) {
            let dirt = Math.random() * 255;
            pData.data[i] = dirt;
            pData.data[i + 1] = dirt * 0.9;
            pData.data[i + 2] = dirt * 0.8;
            pData.data[i + 3] = (Math.random() > 0.65) ? dirt * ruinLvl : 0;
        }
        pCtx.putImageData(pData, 0, 0);

        const dirtPattern = ctx.createPattern(pCan, 'repeat');
        ctx.globalCompositeOperation = 'multiply';
        ctx.fillStyle = dirtPattern;
        ctx.fillRect(0, 0, w, h);

        // Brown/sepia grunge overlay for aged paint feel
        ctx.globalCompositeOperation = 'color-burn';
        ctx.fillStyle = `rgba(110, 90, 75, ${ruinLvl * 0.35})`;
        ctx.fillRect(0, 0, w, h);

        // Outer dark vignette
        ctx.globalCompositeOperation = 'multiply';
        const grad = ctx.createRadialGradient(w/2, h/2, Math.min(w, h)/3, w/2, h/2, Math.max(w, h)/1.2);
        grad.addColorStop(0, 'rgba(0,0,0,0)');
        grad.addColorStop(1, `rgba(0,0,0,${ruinLvl * 0.7})`);
        ctx.fillStyle = grad;
        ctx.fillRect(0, 0, w, h);

        ctx.globalCompositeOperation = 'source-over';
    }

    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

This tool applies a distressed, weathered aesthetic to images by simulating the effect of cracked, peeling, and ruined paint. It breaks the image into randomized triangular shards that can appear slightly displaced, rotated, or entirely removed to reveal a grimy, textured background. Users can customize the level of destruction, including the density of the broken pieces, the amount of cracking, the probability of paint peeling, and the overall intensity of the grunge and dirt overlays. This tool is ideal for graphic designers creating vintage textures, concept artists developing aged environmental assets, or anyone looking to add a gritty, lo-fi, or decayed look to their digital artwork.

Leave a Reply

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