Please bookmark this page to avoid losing your image tool!

Image Cel Shading 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.
/**
 * Applies a cel shading (toon shading) effect to an image.
 * This effect is achieved by reducing the number of colors (posterization)
 * and adding black outlines around the edges.
 *
 * @param {Image} originalImg The original image object to process.
 * @param {number} [levels=5] The number of color levels for posterization. Higher values mean more colors.
 * @param {number} [edgeThreshold=30] The sensitivity for edge detection. Lower values detect more edges.
 * @param {number} [outlineWidth=1] The desired thickness of the black outlines in pixels.
 * @returns {HTMLCanvasElement} A canvas element with the cel-shaded image.
 */
function processImage(originalImg, levels = 5, edgeThreshold = 30, outlineWidth = 1) {
    // 1. SETUP: Create a canvas and draw the original image on it.
    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');
    const width = originalImg.naturalWidth;
    const height = originalImg.naturalHeight;
    canvas.width = width;
    canvas.height = height;
    ctx.drawImage(originalImg, 0, 0);

    // 2. POSTERIZATION: Reduce the number of colors in the image.
    // This creates the distinct color bands typical of cel shading.
    const posterizedImageData = ctx.getImageData(0, 0, width, height);
    const posterizedData = posterizedImageData.data;
    const factor = 255 / (levels - 1);

    for (let i = 0; i < posterizedData.length; i += 4) {
        posterizedData[i] = Math.round(Math.round(posterizedData[i] / factor) * factor); // R
        posterizedData[i + 1] = Math.round(Math.round(posterizedData[i + 1] / factor) * factor); // G
        posterizedData[i + 2] = Math.round(Math.round(posterizedData[i + 2] / factor) * factor); // B
    }

    // 3. EDGE DETECTION: Find edges in the posterized image using the Sobel operator.
    // First, convert the posterized image to grayscale for simpler edge detection.
    const grayData = new Uint8ClampedArray(width * height);
    for (let i = 0; i < posterizedData.length; i += 4) {
        const r = posterizedData[i];
        const g = posterizedData[i + 1];
        const b = posterizedData[i + 2];
        // Use the luminosity formula for grayscale conversion
        grayData[i / 4] = 0.299 * r + 0.587 * g + 0.114 * b;
    }

    // Sobel kernels for detecting horizontal and vertical edges
    const sobelX = [
        [-1, 0, 1],
        [-2, 0, 2],
        [-1, 0, 1]
    ];
    const sobelY = [
        [-1, -2, -1],
        [0, 0, 0],
        [1, 2, 1]
    ];

    // This will store the detected edges (1px wide initially)
    let edgeData = new Uint8ClampedArray(width * height);

    // Apply the Sobel filter
    for (let y = 1; y < height - 1; y++) {
        for (let x = 1; x < width - 1; x++) {
            let gradX = 0;
            let gradY = 0;

            for (let i = -1; i <= 1; i++) {
                for (let j = -1; j <= 1; j++) {
                    const grayVal = grayData[(y + i) * width + (x + j)];
                    gradX += grayVal * sobelX[i + 1][j + 1];
                    gradY += grayVal * sobelY[i + 1][j + 1];
                }
            }

            const magnitude = Math.sqrt(gradX * gradX + gradY * gradY);
            if (magnitude > edgeThreshold) {
                edgeData[y * width + x] = 255;
            }
        }
    }

    // 4. THICKEN OUTLINES (Optional): Use dilation to make outlines thicker.
    if (outlineWidth > 1) {
        const dilatedEdgeData = new Uint8ClampedArray(width * height);
        // The radius determines the thickness.
        const radius = Math.max(0, Math.floor((outlineWidth - 1) / 2));

        if (radius > 0) {
            for (let y = 0; y < height; y++) {
                for (let x = 0; x < width; x++) {
                    // If the original pixel is an edge
                    if (edgeData[y * width + x] === 255) {
                        // Paint a square of size (2*radius+1) around it in the new edge map
                        for (let dy = -radius; dy <= radius; dy++) {
                            for (let dx = -radius; dx <= radius; dx++) {
                                const newY = y + dy;
                                const newX = x + dx;
                                if (newY >= 0 && newY < height && newX >= 0 && newX < width) {
                                    dilatedEdgeData[newY * width + newX] = 255;
                                }
                            }
                        }
                    }
                }
            }
            edgeData = dilatedEdgeData; // Use the new, thicker edge map
        }
    }

    // 5. COMBINE: Create the final image by overlaying black outlines on the posterized image.
    const finalImageData = ctx.createImageData(width, height);
    const finalData = finalImageData.data;

    for (let i = 0; i < finalData.length; i += 4) {
        const pixelIndex = i / 4;
        // If the pixel is marked as an edge, color it black
        if (edgeData[pixelIndex] === 255) {
            finalData[i] = 0; // R
            finalData[i + 1] = 0; // G
            finalData[i + 2] = 0; // B
            finalData[i + 3] = 255; // A (Opaque)
        } else {
            // Otherwise, use the color from the posterized image
            finalData[i] = posterizedData[i];
            finalData[i + 1] = posterizedData[i + 1];
            finalData[i + 2] = posterizedData[i + 2];
            finalData[i + 3] = posterizedData[i + 3];
        }
    }

    // 6. FINALIZE: Put the final image data back onto the canvas and return it.
    ctx.putImageData(finalImageData, 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 Cel Shading Tool applies a cel shading effect to images, simulating a stylized, cartoon-like appearance. This effect is created by reducing the number of colors in the image, leading to distinct color bands (posterization), and by adding bold black outlines around the edges using edge detection techniques. Users can adjust parameters such as the number of color levels and the thickness of the outlines, making it suitable for various applications, including creating unique artwork, enhancing game graphics, or transforming photos for creative projects.

Leave a Reply

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