Please bookmark this page to avoid losing your image tool!

Image Thin Chain Filter

(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, chainSize = "16", bgColor = "#ffffff", colorMode = "original", strokeThickness = "1.5", invertBrightness = "0") {
    // Parse parameters
    const size = parseFloat(chainSize) || 16;
    const thickness = parseFloat(strokeThickness) || 1.5;
    const isMono = String(colorMode).trim().toLowerCase() === 'monochrome';
    const invert = parseInt(invertBrightness, 10) === 1;

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

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

    // Handle background layer
    let bg = String(bgColor).trim();
    if (!bg.startsWith('#') && bg.length === 6) bg = '#' + bg;
    ctx.fillStyle = bg;
    ctx.fillRect(0, 0, w, h);

    // Analyze background to properly map contrast
    const bgData = ctx.getImageData(0, 0, 1, 1).data;
    const bgLum = bgData[0] * 0.299 + bgData[1] * 0.587 + bgData[2] * 0.114;
    const isDarkBg = bgLum < 128; // If background is dark, bright pixels should be opaque

    // Create temporary canvas to read original pixel data
    const tCanvas = document.createElement('canvas');
    tCanvas.width = w;
    tCanvas.height = h;
    const tCtx = tCanvas.getContext('2d', { willReadFrequently: true });
    tCtx.drawImage(originalImg, 0, 0);
    const imgData = tCtx.getImageData(0, 0, w, h).data;

    // Helper to get pixel properties safely
    function getPixel(x, y) {
        const ix = Math.max(0, Math.min(w - 1, Math.round(x)));
        const iy = Math.max(0, Math.min(h - 1, Math.round(y)));
        const idx = (iy * w + ix) * 4;
        return {
            r: imgData[idx],
            g: imgData[idx + 1],
            b: imgData[idx + 2],
            a: imgData[idx + 3]
        };
    }

    // Set drawing styles
    ctx.lineWidth = thickness;
    ctx.lineCap = 'round';
    ctx.lineJoin = 'round';

    // Calculate dimensions for the alternating "chain links"
    const radiusY = Math.max(2, size / 3);
    const frontRadiusX = Math.max(3, size / 2);
    const sideRadiusX = Math.max(1, size / 6);
    const stepX = frontRadiusX;
    
    // Add extra vertical spacing so overlapping chains are readable
    const stepY = radiusY * 2 * 1.25;

    // Draw the image completely constructed out of interconnected chain links
    let rowNum = 0;
    for (let y = stepY / 2; y < h + stepY; y += stepY) {
        let isFront = true;
        
        // Offset alternating rows to form a woven/chainmail look
        let startX = -frontRadiusX;
        if (rowNum % 2 === 1) {
            startX -= stepX / 2;
        }

        for (let x = startX; x < w + frontRadiusX; x += stepX) {
            const p = getPixel(x, y);

            // Skip drawing if pixel is almost fully transparent
            if (p.a > 10) {
                const lum = (0.299 * p.r + 0.587 * p.g + 0.114 * p.b) / 255;
                
                // Determine opacity mapping to create halftone-like shadowing
                let baseAlpha = isDarkBg ? lum : (1.0 - lum);
                if (invert) baseAlpha = 1.0 - baseAlpha;
                
                // Merge opacity with image's original alpha layer
                const finalAlpha = Math.min(1, baseAlpha * 1.5) * (p.a / 255);

                // Discard rendering chains in empty or contrast-nullified coordinate zones
                if (finalAlpha > 0.05) {
                    if (isMono) {
                        ctx.strokeStyle = `rgba(${isDarkBg ? '255,255,255' : '0,0,0'}, ${finalAlpha})`;
                    } else {
                        ctx.strokeStyle = `rgba(${p.r}, ${p.g}, ${p.b}, ${finalAlpha})`;
                    }

                    // Stroke alternating chain link ellipses
                    ctx.beginPath();
                    if (isFront) {
                        ctx.ellipse(x, y, frontRadiusX, radiusY, 0, 0, Math.PI * 2);
                    } else {
                        // Narrow ellipse gives the illusion of a side-facing interlocking link
                        ctx.ellipse(x, y, sideRadiusX, radiusY, 0, 0, Math.PI * 2);
                    }
                    ctx.stroke();
                }
            }
            isFront = !isFront;
        }
        rowNum++;
    }

    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 Thin Chain Filter is a creative tool that transforms your photos into a unique pattern composed of interconnected, chain-like elliptical links. By analyzing the brightness and color of your original image, the tool reconstructs the visual data using varying levels of opacity and stroke thickness to form a woven, chainmail-inspired effect. Users can customize the output with different chain sizes, stroke thicknesses, background colors, and color modes, including a monochrome option. This tool is ideal for artists looking to create stylized textures, digital pattern design, or unique aesthetic overlays for social media and graphic design projects.

Leave a Reply

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