Please bookmark this page to avoid losing your image tool!

Image Skeleton 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, effectMode = 'xray_skeleton', xrayTint = 'true', morphologicalThreshold = 128) {
    const canvas = document.createElement('canvas');
    const width = originalImg.width;
    const height = originalImg.height;
    canvas.width = width;
    canvas.height = height;
    const ctx = canvas.getContext('2d');
    
    // Draw the image to the canvas
    ctx.drawImage(originalImg, 0, 0, width, height);

    const imgData = ctx.getImageData(0, 0, width, height);
    const data = imgData.data;

    // "X-Ray Skeleton" effect for photos/faces
    if (effectMode === 'xray_skeleton') {
        const gray = new Float32Array(width * height);
        for (let i = 0; i < width * height; i++) {
            let r = data[i * 4];
            let g = data[i * 4 + 1];
            let b = data[i * 4 + 2];
            // Convert to grayscale
            gray[i] = 0.299 * r + 0.587 * g + 0.114 * b;
        }

        // Sobel edge detection to simulate glowing bone outlines
        const edges = new Float32Array(width * height);
        const kx = [-1, 0, 1, -2, 0, 2, -1, 0, 1];
        const ky = [-1, -2, -1, 0, 0, 0, 1, 2, 1];

        for (let y = 1; y < height - 1; y++) {
            for (let x = 1; x < width - 1; x++) {
                let px = 0;
                let py = 0;
                for (let j = -1; j <= 1; j++) {
                    for (let i = -1; i <= 1; i++) {
                        const val = gray[(y + j) * width + (x + i)];
                        const kIdx = (j + 1) * 3 + (i + 1);
                        px += val * kx[kIdx];
                        py += val * ky[kIdx];
                    }
                }
                edges[y * width + x] = Math.sqrt(px * px + py * py);
            }
        }

        // Apply X-ray / spooky effect and blend with edges
        for (let i = 0; i < width * height; i++) {
            // Invert the grayscale to make background dark and features bright (like bones)
            let inv = 255 - gray[i];
            
            // Enhance contrast significantly for skeletal X-ray look
            let contrast = 1.3;
            let xrayLuma = (inv - 128) * contrast + 128;
            
            // Add the glowing edges onto the x-ray
            let finalLuma = xrayLuma + edges[i] * 0.5;
            finalLuma = Math.max(0, Math.min(255, finalLuma));

            if (xrayTint === 'true') {
                // Typical cyan/blueish medical x-ray tint
                data[i * 4] = finalLuma * 0.75;       // Red
                data[i * 4 + 1] = finalLuma * 0.95;   // Green
                data[i * 4 + 2] = finalLuma * 1.0;    // Blue
            } else {
                // Pure spooky black & white
                data[i * 4] = finalLuma;
                data[i * 4 + 1] = finalLuma;
                data[i * 4 + 2] = finalLuma;
            }
            data[i * 4 + 3] = 255;
        }
    } 
    // "Morphological Skeleton" effect for shapes, silhouettes, and typography (Zhang-Suen Thinning)
    else if (effectMode === 'morphological_skeleton') {
        const threshold = Number(morphologicalThreshold);
        const binary = new Uint8Array(width * height);
        
        for (let i = 0; i < width * height; i++) {
            const r = data[i * 4];
            const g = data[i * 4 + 1];
            const b = data[i * 4 + 2];
            const lum = 0.299 * r + 0.587 * g + 0.114 * b;
            // Assumes objects are dark on light backgrounds.
            binary[i] = lum < threshold ? 1 : 0;
        }

        let hasChanged = true;
        const marker = new Uint8Array(width * height);
        
        // Safety upper limit so giant images won't crash the browser
        let iterations = 0; 

        while (hasChanged && iterations < 200) {
            hasChanged = false;
            iterations++;

            // Thinning Step 1
            for (let y = 1; y < height - 1; y++) {
                for (let x = 1; x < width - 1; x++) {
                    let idx = y * width + x;
                    if (binary[idx] === 0) continue;

                    let p2 = binary[(y - 1) * width + x];
                    let p3 = binary[(y - 1) * width + x + 1];
                    let p4 = binary[y * width + x + 1];
                    let p5 = binary[(y + 1) * width + x + 1];
                    let p6 = binary[(y + 1) * width + x];
                    let p7 = binary[(y + 1) * width + x - 1];
                    let p8 = binary[y * width + x - 1];
                    let p9 = binary[(y - 1) * width + x - 1];

                    let B = p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9;
                    if (B >= 2 && B <= 6) {
                        let A = 0;
                        if (p2 == 0 && p3 == 1) A++;
                        if (p3 == 0 && p4 == 1) A++;
                        if (p4 == 0 && p5 == 1) A++;
                        if (p5 == 0 && p6 == 1) A++;
                        if (p6 == 0 && p7 == 1) A++;
                        if (p7 == 0 && p8 == 1) A++;
                        if (p8 == 0 && p9 == 1) A++;
                        if (p9 == 0 && p2 == 1) A++;

                        if (A === 1 && (p2 * p4 * p6 === 0) && (p4 * p6 * p8 === 0)) {
                            marker[idx] = 1;
                            hasChanged = true;
                        }
                    }
                }
            }

            for (let i = 0; i < width * height; i++) {
                if (marker[i] === 1) {
                    binary[i] = 0;
                    marker[i] = 0;
                }
            }

            // Thinning Step 2
            let step2Changed = false;
            for (let y = 1; y < height - 1; y++) {
                for (let x = 1; x < width - 1; x++) {
                    let idx = y * width + x;
                    if (binary[idx] === 0) continue;

                    let p2 = binary[(y - 1) * width + x];
                    let p3 = binary[(y - 1) * width + x + 1];
                    let p4 = binary[y * width + x + 1];
                    let p5 = binary[(y + 1) * width + x + 1];
                    let p6 = binary[(y + 1) * width + x];
                    let p7 = binary[(y + 1) * width + x - 1];
                    let p8 = binary[y * width + x - 1];
                    let p9 = binary[(y - 1) * width + x - 1];

                    let B = p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9;
                    if (B >= 2 && B <= 6) {
                        let A = 0;
                        if (p2 == 0 && p3 == 1) A++;
                        if (p3 == 0 && p4 == 1) A++;
                        if (p4 == 0 && p5 == 1) A++;
                        if (p5 == 0 && p6 == 1) A++;
                        if (p6 == 0 && p7 == 1) A++;
                        if (p7 == 0 && p8 == 1) A++;
                        if (p8 == 0 && p9 == 1) A++;
                        if (p9 == 0 && p2 == 1) A++;

                        if (A === 1 && (p2 * p4 * p8 === 0) && (p2 * p6 * p8 === 0)) {
                            marker[idx] = 1;
                            step2Changed = true;
                        }
                    }
                }
            }

            for (let i = 0; i < width * height; i++) {
                if (marker[i] === 1) {
                    binary[i] = 0;
                    marker[i] = 0;
                }
            }
            hasChanged = hasChanged || step2Changed;
        }

        // Draw structural skeleton as glowing white on black canvas
        for (let i = 0; i < width * height; i++) {
            let v = binary[i] === 1 ? 255 : 0;
            data[i * 4] = v;
            data[i * 4 + 1] = v;
            data[i * 4 + 2] = v;
            data[i * 4 + 3] = 255;
        }
    }

    ctx.putImageData(imgData, 0, 0);
    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 Skeleton Filter Effect Tool allows users to apply specialized skeletal visual effects to their images through two distinct modes. The ‘X-Ray Skeleton’ mode transforms photos or portraits by simulating a medical x-ray appearance, utilizing edge detection and color tinting to create a glowing, spooky aesthetic. The ‘Morphological Skeleton’ mode is designed for shapes, silhouettes, and typography, using a thinning algorithm to reduce complex shapes down to their essential structural outlines. This tool is ideal for digital artists creating spooky or anatomical themed content, graphic designers working with minimalist line art, and creators looking for unique stylistic filters for social media.

Leave a Reply

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