Please bookmark this page to avoid losing your image tool!

Image Cracked Emulsion 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.
function processImage(originalImg, numCracks = 5, crackColor = "rgba(0,0,0,0.7)",
                       crackWidthMultiplier = 1.0, shadowOffset = 1, shadowColorLight = "rgba(255,255,255,0.3)",
                       shadowColorDark = "rgba(0,0,0,0.3)") {

    const pNumCracks = parseInt(String(numCracks), 10);
    const pCrackColor = String(crackColor);
    const pCrackWidthMultiplier = parseFloat(String(crackWidthMultiplier));
    const pShadowOffset = parseFloat(String(shadowOffset));
    const pShadowColorLight = String(shadowColorLight);
    const pShadowColorDark = String(shadowColorDark);

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

    if (canvas.width === 0 || canvas.height === 0) {
        // Handle invalid image dimensions
        console.error("Image has zero width or height.");
        return canvas; // Return an empty canvas
    }

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

    // Helper class for Cracks
    class Crack {
        constructor(startX, startY, startAngle, canvasWidth, canvasHeight, baseWidth, globalCrackWidthMultiplier) {
            this.points = [{ x: startX, y: startY }];
            this.currentAngle = startAngle;
            this.canvasWidth = canvasWidth;
            this.canvasHeight = canvasHeight;
            this.alive = true;
            this.baseWidth = baseWidth; // Specific to this crack path
            this.globalCrackWidthMultiplier = globalCrackWidthMultiplier; // From function params
            this.segmentsDrawn = 0;
            this.maxSegments = 30 + Math.random() * 70; // Crack length: 30 to 100 segments
        }

        step() {
            if (!this.alive || this.segmentsDrawn >= this.maxSegments) {
                this.alive = false;
                return false;
            }

            const lastPoint = this.points[this.points.length - 1];
            const baseSegmentLength = Math.max(1, Math.min(this.canvasWidth, this.canvasHeight) / 75);
            const segmentLength = baseSegmentLength + Math.random() * baseSegmentLength;

            this.currentAngle += (Math.random() - 0.5) * 0.7; // Angle variation

            let newX = lastPoint.x + Math.cos(this.currentAngle) * segmentLength;
            let newY = lastPoint.y + Math.sin(this.currentAngle) * segmentLength;

            let attempts = 0;
            const maxAttempts = 5;
            while ((newX < 0 || newX > this.canvasWidth || newY < 0 || newY > this.canvasHeight) && attempts < maxAttempts) {
                this.currentAngle += (Math.PI / 3) * (Math.random() > 0.5 ? 1 : -1); // Turn sharper
                newX = lastPoint.x + Math.cos(this.currentAngle) * segmentLength;
                newY = lastPoint.y + Math.sin(this.currentAngle) * segmentLength;
                attempts++;
            }

            if (newX < 0 || newX > this.canvasWidth || newY < 0 || newY > this.canvasHeight) {
                this.alive = false;
                return false;
            }

            this.points.push({ x: newX, y: newY });
            this.segmentsDrawn++;

            if (Math.random() < 0.005 * this.segmentsDrawn) {
                this.alive = false;
            }
            return true;
        }

        canBranch(totalCrackObjects, maxCrackObjectsAllowed) {
            const branchProbabilityFactor = Math.max(0.1, (1 - totalCrackObjects / maxCrackObjectsAllowed));
            const branchProbability = 0.04 * branchProbabilityFactor;
            return this.alive && this.points.length > 2 && this.segmentsDrawn < this.maxSegments * 0.8 && Math.random() < branchProbability;
        }

        getSegmentWidth(segmentIndex) {
            const taperFactor = 1 - (segmentIndex / (this.points.length || 1)) * 0.7;
            const randomVariation = 0.7 + Math.random() * 0.6;
            return Math.max(0.5, this.baseWidth * this.globalCrackWidthMultiplier * taperFactor * randomVariation);
        }
    }

    let cracks = [];
    let activeCracks = [];

    const validatedNumCracks = Math.max(1, pNumCracks || 5);

    for (let i = 0; i < validatedNumCracks; i++) {
        let startX, startY, startAngle;
        if (Math.random() < 0.6) { // Start from edge
            const edge = Math.floor(Math.random() * 4);
            if (edge === 0) { startX = Math.random() * canvas.width; startY = 0; startAngle = Math.random() * Math.PI; }
            else if (edge === 1) { startX = canvas.width; startY = Math.random() * canvas.height; startAngle = Math.PI / 2 + Math.random() * Math.PI; }
            else if (edge === 2) { startX = Math.random() * canvas.width; startY = canvas.height; startAngle = Math.PI + Math.random() * Math.PI; }
            else { startX = 0; startY = Math.random() * canvas.height; startAngle = -Math.PI / 2 + Math.random() * Math.PI; }
        } else { // Start from random internal point
            startX = Math.random() * canvas.width;
            startY = Math.random() * canvas.height;
            startAngle = Math.random() * 2 * Math.PI;
        }
        const baseWidth = (0.8 + Math.random() * 1.2);
        activeCracks.push(new Crack(startX, startY, startAngle, canvas.width, canvas.height, baseWidth, pCrackWidthMultiplier));
    }

    const maxCrackObjects = validatedNumCracks * 8;
    let totalCrackObjects = activeCracks.length;
    let iterations = 0;
    const maxIterations = 250;

    while (activeCracks.length > 0 && iterations < maxIterations && totalCrackObjects < maxCrackObjects) {
        for (let i = activeCracks.length - 1; i >= 0; i--) {
            const crack = activeCracks[i];
            if (!crack.step()) {
                cracks.push(crack);
                activeCracks.splice(i, 1);
            } else if (crack.canBranch(totalCrackObjects, maxCrackObjects)) {
                const lastPoint = crack.points[crack.points.length - 1];
                const branchAngle = crack.currentAngle + (Math.random() - 0.5) * Math.PI / 1.5; // Branch off at wider angle
                const branchBaseWidth = crack.baseWidth * (0.4 + Math.random() * 0.4); // Branches are thinner
                activeCracks.push(new Crack(lastPoint.x, lastPoint.y, branchAngle, canvas.width, canvas.height, branchBaseWidth, pCrackWidthMultiplier));
                totalCrackObjects++;
                if (totalCrackObjects >= maxCrackObjects) break; // Optimization
            }
        }
        if (totalCrackObjects >= maxCrackObjects) break; // Optimization
        iterations++;
    }
    cracks.push(...activeCracks);

    // Draw cracks
    cracks.forEach(crack => {
        if (crack.points.length < 2) return;
        ctx.lineCap = "round";
        ctx.lineJoin = "round";

        for (let i = 1; i < crack.points.length; i++) {
            const p1 = crack.points[i-1];
            const p2 = crack.points[i];
            const currentLineWidth = crack.getSegmentWidth(i);
            if (currentLineWidth <= 0.1) continue; // Skip negligible lines

            if (pShadowOffset > 0) {
                ctx.lineWidth = currentLineWidth;
                // Dark shadow
                ctx.strokeStyle = pShadowColorDark;
                ctx.beginPath();
                ctx.moveTo(p1.x + pShadowOffset, p1.y + pShadowOffset);
                ctx.lineTo(p2.x + pShadowOffset, p2.y + pShadowOffset);
                ctx.stroke();
                // Light highlight
                ctx.strokeStyle = pShadowColorLight;
                ctx.beginPath();
                ctx.moveTo(p1.x - pShadowOffset, p1.y - pShadowOffset);
                ctx.lineTo(p2.x - pShadowOffset, p2.y - pShadowOffset);
                ctx.stroke();
            }

            // Main crack line
            ctx.lineWidth = currentLineWidth;
            ctx.strokeStyle = pCrackColor;
            ctx.beginPath();
            ctx.moveTo(p1.x, p1.y);
            ctx.lineTo(p2.x, p2.y);
            ctx.stroke();
        }
    });

    // Chipping effect
    const chipDensity = 0.15;
    const chipColor = pCrackColor;
    const maxChipOffsetFactor = 2.5; // Multiplier for baseCrackWidthAtPoint for offset

    cracks.forEach(crack => {
        if (crack.points.length < 2) return;
        for (let i = 0; i < crack.points.length; i += Math.floor(Math.random() * 5 + 4)) { // Every 4-8 points
            if (Math.random() < chipDensity) {
                const p = crack.points[i];
                const baseCrackWidthAtPoint = crack.getSegmentWidth(i);
                if (baseCrackWidthAtPoint <= 0.1) continue;

                const numMicroChips = Math.floor(Math.random() * 3) + 1; // 1 to 3 micro chips
                for (let j = 0; j < numMicroChips; j++) {
                    const microChipSize = Math.max(0.5, (0.2 + Math.random() * 0.5) * baseCrackWidthAtPoint + Math.random() * 0.5);
                    const offsetX = (Math.random() - 0.5) * baseCrackWidthAtPoint * maxChipOffsetFactor;
                    const offsetY = (Math.random() - 0.5) * baseCrackWidthAtPoint * maxChipOffsetFactor;

                    ctx.fillStyle = chipColor;
                    ctx.beginPath();
                    ctx.arc(p.x + offsetX, p.y + offsetY, microChipSize, 0, Math.PI * 2);
                    ctx.fill();
                }
            }
        }
    });

    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 Cracked Emulsion Filter Effect Tool allows users to apply a unique crack effect to images, mimicking the appearance of cracked emulsion or damaged photographic prints. Users can customize various parameters including the number of cracks, their color, thickness, and shadow effects, resulting in a highly stylized and artistic transformation of their images. This tool can be used in creative projects such as graphic design, digital art, photography enhancements, or simply for personal expression, making images stand out with a vintage or distressed look.

Leave a Reply

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