Please bookmark this page to avoid losing your image tool!

Image Celtic Knot Pattern Filter Effect

(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, knotColor = '#3a3a3a', knotOpacity = 0.3, knotSize = 30, knotWidth = 4, imageTreatment = 'grayscale', blendMode = 'overlay') {
    const outputCanvas = document.createElement('canvas');
    const outputCtx = outputCanvas.getContext('2d');

    const w = originalImg.naturalWidth || originalImg.width;
    const h = originalImg.naturalHeight || originalImg.height;
    outputCanvas.width = w;
    outputCanvas.height = h;

    // 1. Image Treatment
    if (imageTreatment === 'grayscale') {
        outputCtx.filter = 'grayscale(100%)';
        outputCtx.drawImage(originalImg, 0, 0, w, h);
        outputCtx.filter = 'none'; // Reset filter
    } else if (imageTreatment === 'sepia') {
        outputCtx.filter = 'sepia(100%)';
        outputCtx.drawImage(originalImg, 0, 0, w, h);
        outputCtx.filter = 'none'; // Reset filter
    } else { // 'none' or any other value
        outputCtx.drawImage(originalImg, 0, 0, w, h);
    }

    // Validate and adjust knot parameters
    let currentKnotSize = Math.max(20, Math.floor(knotSize / 2) * 2); // Ensure even and min size
    let currentKnotWidth = Math.max(1, knotWidth);
    
    const subSize = currentKnotSize / 2;
    // Ensure knotWidth is not too large for subSize, to prevent visual errors in gaps
    if (currentKnotWidth * 1.5 > subSize) {
        currentKnotWidth = Math.max(1, Math.floor(subSize / 1.5));
    }


    // 2. Create Knot Tile Pattern
    const knotTileCanvas = document.createElement('canvas');
    knotTileCanvas.width = currentKnotSize;
    knotTileCanvas.height = currentKnotSize;
    const knotTileCtx = knotTileCanvas.getContext('2d');
    knotTileCtx.strokeStyle = knotColor;

    // Helper function to draw a sub-knot (a single crossing)
    function drawSubKnot(ctx, x_offset, y_offset, singleCellSize, singleCellKnotWidth, isHorizontalOver) {
        ctx.lineCap = 'round';
        ctx.lineWidth = singleCellKnotWidth;

        const halfCellSize = singleCellSize / 2;
        // Space for the "under" pass, proportional to knot width
        const spaceAroundCrossingLine = singleCellKnotWidth * 0.25; 
        const halfCrossingLineWidth = singleCellKnotWidth / 2;

        if (isHorizontalOver) {
            // Horizontal strand (full)
            ctx.beginPath();
            ctx.moveTo(x_offset, y_offset + halfCellSize);
            ctx.lineTo(x_offset + singleCellSize, y_offset + halfCellSize);
            ctx.stroke();

            // Vertical strand (broken)
            ctx.beginPath();
            ctx.moveTo(x_offset + halfCellSize, y_offset);
            ctx.lineTo(x_offset + halfCellSize, y_offset + halfCellSize - halfCrossingLineWidth - spaceAroundCrossingLine);
            ctx.stroke();

            ctx.beginPath();
            ctx.moveTo(x_offset + halfCellSize, y_offset + halfCellSize + halfCrossingLineWidth + spaceAroundCrossingLine);
            ctx.lineTo(x_offset + halfCellSize, y_offset + singleCellSize);
            ctx.stroke();
        } else { // VerticalOverHorizontal
            // Vertical strand (full)
            ctx.beginPath();
            ctx.moveTo(x_offset + halfCellSize, y_offset);
            ctx.lineTo(x_offset + halfCellSize, y_offset + singleCellSize);
            ctx.stroke();

            // Horizontal strand (broken)
            ctx.beginPath();
            ctx.moveTo(x_offset, y_offset + halfCellSize);
            ctx.lineTo(x_offset + halfCellSize - halfCrossingLineWidth - spaceAroundCrossingLine, y_offset + halfCellSize);
            ctx.stroke();

            ctx.beginPath();
            ctx.moveTo(x_offset + halfCellSize + halfCrossingLineWidth + spaceAroundCrossingLine, y_offset + halfCellSize);
            ctx.lineTo(x_offset + singleCellSize, y_offset + halfCellSize);
            ctx.stroke();
        }
    }

    // Draw a 2x2 pattern of sub-knots with alternating crossings
    drawSubKnot(knotTileCtx, 0,         0,         subSize, currentKnotWidth, true);  // Top-left: H over V
    drawSubKnot(knotTileCtx, subSize,   0,         subSize, currentKnotWidth, false); // Top-right: V over H
    drawSubKnot(knotTileCtx, 0,         subSize,   subSize, currentKnotWidth, false); // Bottom-left: V over H
    drawSubKnot(knotTileCtx, subSize,   subSize,   subSize, currentKnotWidth, true);  // Bottom-right: H over V

    // 3. Apply Knot Pattern to Output Canvas
    const knotPattern = outputCtx.createPattern(knotTileCanvas, 'repeat');
    if (knotPattern) {
        outputCtx.globalAlpha = Math.min(1, Math.max(0, knotOpacity)); // Clamp opacity 0-1
        // Validate blend mode input, default to 'source-over' if invalid
        const validBlendModes = ['source-over', 'source-in', 'source-out', 'source-atop',
                               'destination-over', 'destination-in', 'destination-out', 'destination-atop',
                               'lighter', 'copy', 'xor', 'multiply', 'screen', 'overlay', 'darken',
                               'lighten', 'color-dodge', 'color-burn', 'hard-light', 'soft-light',
                               'difference', 'exclusion', 'hue', 'saturation', 'color', 'luminosity'];
        if (validBlendModes.includes(blendMode)) {
            outputCtx.globalCompositeOperation = blendMode;
        } else {
            outputCtx.globalCompositeOperation = 'overlay'; // Default fallback
        }
        
        outputCtx.fillStyle = knotPattern;
        outputCtx.fillRect(0, 0, w, h);

        // Reset canvas context properties
        outputCtx.globalAlpha = 1.0;
        outputCtx.globalCompositeOperation = 'source-over';
    } else {
        console.error("Failed to create knot pattern.");
    }
    
    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 Celtic Knot Pattern Filter Effect tool allows users to apply a decorative Celtic knot pattern overlay to their images. Users can customize the color, opacity, size, and width of the knot pattern, as well as choose different image treatments such as grayscale or sepia. This tool is useful for enhancing images with artistic flair, creating unique backgrounds, or producing visually appealing graphics for projects, social media, or personal use.

Leave a Reply

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