Please bookmark this page to avoid losing your image tool!

Image Electrical Wiring 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, wireColor = "#333333", wireThickness = 1, edgeThreshold = 70, numWires = 300, maxWireSegments = 20, segmentLength = 8, turnRandomness = Math.PI / 8) {
    const width = originalImg.width;
    const height = originalImg.height;

    if (width === 0 || height === 0) {
        console.warn("Image has zero width or height.");
        const emptyCanvas = document.createElement('canvas');
        emptyCanvas.width = width;
        emptyCanvas.height = height;
        return emptyCanvas;
    }

    // 1. Create temp canvas for image processing (grayscale, blur)
    const tempCanvas = document.createElement('canvas');
    tempCanvas.width = width;
    tempCanvas.height = height;
    const tempCtx = tempCanvas.getContext('2d', { willReadFrequently: true });
    
    try {
        tempCtx.drawImage(originalImg, 0, 0, width, height);
    } catch (e) {
        console.error("Error drawing original image to temporary canvas:", e);
        const errorCanvas = document.createElement('canvas');
        errorCanvas.width = width;
        errorCanvas.height = height;
        // Optionally draw something on errorCanvas or leave blank
        return errorCanvas;
    }

    let imageData;
    try {
        imageData = tempCtx.getImageData(0, 0, width, height);
    } catch (e) {
        console.error("Error getting image data (cross-origin issue?):", e);
        // Fallback: return canvas with original image if pixel manipulation is not possible
        const fallbackCanvas = document.createElement('canvas');
        fallbackCanvas.width = width;
        fallbackCanvas.height = height;
        const fallbackCtx = fallbackCanvas.getContext('2d');
        try {
            fallbackCtx.drawImage(originalImg, 0, 0, width, height);
        } catch (drawError) {
            console.error("Error drawing original image to fallback canvas:", drawError);
        }
        return fallbackCanvas;
    }

    // 2. Grayscale
    const grayData = new Uint8ClampedArray(width * height);
    for (let i = 0; i < imageData.data.length; i += 4) {
        const r = imageData.data[i];
        const g = imageData.data[i + 1];
        const b = imageData.data[i + 2];
        const gray = 0.299 * r + 0.587 * g + 0.114 * b;
        grayData[i / 4] = gray;
    }

    // 3. Box Blur (on grayData) - simple 3x3 blur
    const blurredGrayData = new Uint8ClampedArray(width * height);
    if (width < 3 || height < 3) { 
        // For very small images, skip blur or copy grayData
        for(let i=0; i < grayData.length; i++) blurredGrayData[i] = grayData[i];
    } else {
        for (let y = 0; y < height; y++) {
            for (let x = 0; x < width; x++) {
                let sum = 0;
                let count = 0;
                for (let dy = -1; dy <= 1; dy++) {
                    for (let dx = -1; dx <= 1; dx++) {
                        const nx = x + dx;
                        const ny = y + dy;
                        if (nx >= 0 && nx < width && ny >= 0 && ny < height) {
                            sum += grayData[ny * width + nx]; // Blur from original grayData
                            count++;
                        }
                    }
                }
                blurredGrayData[y * width + x] = count > 0 ? sum / count : grayData[y * width + x];
            }
        }
    }

    // 4. Sobel Edge Detection (on blurredGrayData)
    const magnitudes = new Float32Array(width * height); // Initialized to 0
    const directions = new Float32Array(width * height); // Initialized to 0
    const Gx_kernel = [[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]];
    const Gy_kernel = [[-1, -2, -1], [0, 0, 0], [1, 2, 1]];

    if (width >= 3 && height >= 3) { // Sobel needs at least 3x3 image
        for (let y = 1; y < height - 1; y++) {
            for (let x = 1; x < width - 1; x++) {
                let sumX = 0;
                let sumY = 0;
                for (let ky = -1; ky <= 1; ky++) {
                    for (let kx = -1; kx <= 1; kx++) {
                        const val = blurredGrayData[(y + ky) * width + (x + kx)];
                        sumX += val * Gx_kernel[ky + 1][kx + 1];
                        sumY += val * Gy_kernel[ky + 1][kx + 1];
                    }
                }
                magnitudes[y * width + x] = Math.sqrt(sumX * sumX + sumY * sumY);
                directions[y * width + x] = Math.atan2(sumY, sumX); // Angle in radians: -PI to PI
            }
        }
    }

    // 5. Prepare output canvas
    const outputCanvas = document.createElement('canvas');
    outputCanvas.width = width;
    outputCanvas.height = height;
    const ctx = outputCanvas.getContext('2d');
    try {
        ctx.drawImage(originalImg, 0, 0, width, height); // Draw original image as background
    } catch(e) {
      // Should not happen if tempCanvas drawImage succeeded, but as a safeguard
      console.error("Error drawing original image to output canvas:", e);
    }


    // 6. Draw Wires
    if (width < 3 || height < 3) { // Not enough data for wires if image is too small
        return outputCanvas; // Return original image drawn on canvas
    }

    ctx.strokeStyle = wireColor;
    ctx.lineWidth = wireThickness;
    ctx.lineCap = "round";
    ctx.lineJoin = "round";

    // Attempt to find start points for wires
    // Max attempts: 10% of numWires, but at least 10, and not more than numWires itself
    const maxStartAttempts = Math.min(numWires, Math.max(10, Math.floor(numWires * 0.2))); 

    for (let i = 0; i < numWires; i++) {
        let startX = 0, startY = 0;
        let foundStart = false;
        for (let attempt = 0; attempt < maxStartAttempts; attempt++) {
            // Pick random point, ensuring it's within the Sobel-processed area [1, width-2]x[1, height-2]
            startX = Math.floor(Math.random() * (width - 2)) + 1;
            startY = Math.floor(Math.random() * (height - 2)) + 1;
            
            if (magnitudes[startY * width + startX] > edgeThreshold) {
                foundStart = true;
                break;
            }
        }

        if (!foundStart) continue;

        let currentX = startX;
        let currentY = startY;
        
        // Initial angle is one of the two edge directions at start point
        let currentAngle = directions[startY * width + startX] + Math.PI / 2; // Edge direction
        if (Math.random() < 0.5) { 
             currentAngle += Math.PI; // Randomly pick one of the two opposite directions
        }

        ctx.beginPath();
        ctx.moveTo(currentX, currentY);
        let segmentsDrawn = 0;

        for (let j = 0; j < maxWireSegments; j++) {
            // Ensure current coordinates are within valid Sobel range for reading direction
            const ix = Math.max(1, Math.min(width - 2, Math.round(currentX)));
            const iy = Math.max(1, Math.min(height - 2, Math.round(currentY)));

            const gradAngle = directions[iy * width + ix]; // Gradient direction at current point
            
            // Determine two possible edge directions (perpendicular to gradient)
            let edgeDir1 = gradAngle + Math.PI / 2;
            let edgeDir2 = gradAngle - Math.PI / 2; 

            // Normalize angles to [0, 2*PI) for consistent comparison
            const normalizeAngle = (angle) => (angle % (2 * Math.PI) + 2 * Math.PI) % (2 * Math.PI);
            
            let cAngleNorm = normalizeAngle(currentAngle);
            edgeDir1 = normalizeAngle(edgeDir1);
            edgeDir2 = normalizeAngle(edgeDir2);
            
            // Calculate angular difference to choose path of least resistance (inertia)
            let diff1 = Math.abs(cAngleNorm - edgeDir1);
            if (diff1 > Math.PI) diff1 = 2 * Math.PI - diff1; // Ensure shortest angle
            
            let diff2 = Math.abs(cAngleNorm - edgeDir2);
            if (diff2 > Math.PI) diff2 = 2 * Math.PI - diff2;

            let chosenEdgeAngle = (diff1 < diff2) ? edgeDir1 : edgeDir2;
            
            // Add random perturbation to the chosen angle
            const randomPerturbation = (Math.random() - 0.5) * 2 * turnRandomness;
            currentAngle = chosenEdgeAngle + randomPerturbation;

            const nextX = currentX + segmentLength * Math.cos(currentAngle);
            const nextY = currentY + segmentLength * Math.sin(currentAngle);
            
            const nextIx = Math.round(nextX);
            const nextIy = Math.round(nextY);

            // Boundary checks for the next point
            if (nextIx <= 0 || nextIx >= width - 1 || nextIy <= 0 || nextIy >= height - 1 ) {
                 break; // Stop if wire goes out of Sobel-valid bounds
            }
            // Stop if edge strength at next point is too low
            if (magnitudes[nextIy * width + nextIx] < edgeThreshold * 0.5) { 
                break;
            }
            
            ctx.lineTo(nextX, nextY);
            segmentsDrawn++;
            currentX = nextX;
            currentY = nextY;
        }
        
        if (segmentsDrawn > 0) { // Only stroke if the path has at least one segment
            ctx.stroke();
        }
    }

    return outputCanvas;
}

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 Electrical Wiring Filter Effect Tool allows users to transform standard images into artistic representations that resemble electrical wiring patterns. This tool processes images by applying grayscale conversion, blurring, and edge detection to simulate the look of wires following the contours of the original image. Users can customize various parameters such as wire color, thickness, the number of wires, and their randomness, making it suitable for creative projects, design mockups, and digital artwork that need a unique, technical appearance of wiring effects.

Leave a Reply

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