Please bookmark this page to avoid losing your image tool!

Image Cel Shader Application

(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 outlines to the edges of objects.
 *
 * @param {HTMLImageElement} originalImg The original image element to process.
 * @param {number} [levels=4] The number of color levels for posterization. A lower number creates a flatter look. Must be >= 2.
 * @param {number} [edgeThreshold=80] The sensitivity for edge detection. Higher values mean fewer, more pronounced outlines. Range is typically 0-255.
 * @param {string} [edgeColor='#000000'] The color of the outlines in hex format (e.g., '#000000' for black).
 * @returns {HTMLCanvasElement} A canvas element with the cel shaded image.
 */
function processImage(originalImg, levels = 4, edgeThreshold = 80, edgeColor = '#000000') {
    // 1. Setup: Create canvas, get context, and draw the original image
    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d', { willReadFrequently: true });
    const width = originalImg.naturalWidth;
    const height = originalImg.naturalHeight;
    canvas.width = width;
    canvas.height = height;

    ctx.drawImage(originalImg, 0, 0, width, height);

    // Get the pixel data of the original image
    const originalImageData = ctx.getImageData(0, 0, width, height);
    const originalData = originalImageData.data;
    
    // Create a new data array for the output image
    const outputData = new Uint8ClampedArray(originalData.length);

    // --- Helper functions ---

    // Parse hex color string to an RGB object
    const hexToRgb = (hex) => {
        const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
        return result ? {
            r: parseInt(result[1], 16),
            g: parseInt(result[2], 16),
            b: parseInt(result[3], 16)
        } : { r: 0, g: 0, b: 0 }; // Default to black on failure
    };

    // Get the grayscale value of a pixel at coordinates (x, y)
    const getGray = (x, y, data, width) => {
        if (x < 0 || x >= width || y < 0 || y >= height) return 0;
        const i = (y * width + x) * 4;
        // Using the luminosity method for grayscale conversion
        return 0.299 * data[i] + 0.587 * data[i + 1] + 0.114 * data[i + 2];
    };
    
    // --- Main Processing ---
    
    // Clamp levels to a safe value
    const numLevels = Math.max(2, Math.floor(levels));
    const posterizationFactor = 255 / (numLevels - 1);
    const parsedEdgeColor = hexToRgb(edgeColor);

    // Sobel kernels for edge detection
    const Gx = [
        [-1, 0, 1],
        [-2, 0, 2],
        [-1, 0, 1]
    ];
    const Gy = [
        [-1, -2, -1],
        [0, 0, 0],
        [1, 2, 1]
    ];

    // 2. Iterate through each pixel to apply effects
    for (let y = 0; y < height; y++) {
        for (let x = 0; x < width; x++) {
            const index = (y * width + x) * 4;
            
            // --- Edge Detection using Sobel Operator ---
            let isEdge = false;
            // Avoid borders to stay within image bounds for the 3x3 kernel
            if (y > 0 && y < height - 1 && x > 0 && x < width - 1) {
                let gxValue = 0;
                let gyValue = 0;
                
                for (let ky = -1; ky <= 1; ky++) {
                    for (let kx = -1; kx <= 1; kx++) {
                        const gray = getGray(x + kx, y + ky, originalData, width);
                        gxValue += Gx[ky + 1][kx + 1] * gray;
                        gyValue += Gy[ky + 1][kx + 1] * gray;
                    }
                }
                
                const magnitude = Math.sqrt(gxValue * gxValue + gyValue * gyValue);
                if (magnitude > edgeThreshold) {
                    isEdge = true;
                }
            }
            
            // --- Apply color based on whether it's an edge or not ---
            if (isEdge) {
                // Apply edge color
                outputData[index] = parsedEdgeColor.r;
                outputData[index + 1] = parsedEdgeColor.g;
                outputData[index + 2] = parsedEdgeColor.b;
                outputData[index + 3] = 255; // Full opacity for outlines
            } else {
                // --- Posterization ---
                const r = originalData[index];
                const g = originalData[index + 1];
                const b = originalData[index + 2];
                
                outputData[index] = Math.round(Math.round(r / posterizationFactor) * posterizationFactor);
                outputData[index + 1] = Math.round(Math.round(g / posterizationFactor) * posterizationFactor);
                outputData[index + 2] = Math.round(Math.round(b / posterizationFactor) * posterizationFactor);
                outputData[index + 3] = originalData[index + 3]; // Preserve original alpha
            }
        }
    }

    // 3. Put the modified pixel data back onto the canvas
    const outputImageData = new ImageData(outputData, width, height);
    ctx.putImageData(outputImageData, 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 Shader Application tool allows users to apply a cel shading (toon shading) effect to images. This effect is created by reducing the number of colors in the image and adding pronounced outlines to the edges of objects. Users can adjust the number of color levels, edge detection sensitivity, and the color of the outlines. This tool is ideal for artists and designers looking to create stylized graphics, enhance illustrations, or transform photographs into a cartoon-like format.

Leave a Reply

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