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!
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.