You can edit the below JavaScript code to customize the image tool.
Apply Changes
/**
* Converts a photo into a cartoon-style image by applying color posterization and edge detection.
*
* @param {Image} originalImg The original Image object to be processed.
* @param {number} colorLevels The number of color levels for posterization. Fewer levels result in a more stylized, flatter look.
* @param {number} blurRadius The radius of the Gaussian blur applied before posterization to smooth color areas.
* @param {number} edgeThreshold The sensitivity for edge detection. A lower value detects more lines.
* @param {string} edgeColor A comma-separated RGB string (e.g., '0,0,0') for the color of the detected edges.
* @returns {HTMLCanvasElement} A canvas element with the cartoonized image.
*/
function processImage(originalImg, colorLevels = 8, blurRadius = 5, edgeThreshold = 80, edgeColor = '0,0,0') {
const width = originalImg.naturalWidth;
const height = originalImg.naturalHeight;
// Create the final canvas that will be returned.
const canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext('2d');
// Parse the edge color string into R, G, B components.
const [r, g, b] = edgeColor.split(',').map(c => parseInt(c.trim(), 10));
// --- Step 1: Create the color-posterized base layer ---
const colorCanvas = document.createElement('canvas');
colorCanvas.width = width;
colorCanvas.height = height;
const colorCtx = colorCanvas.getContext('2d', {
willReadFrequently: true
});
// Apply a blur to smooth out details and help colors clump together.
if (blurRadius > 0) {
colorCtx.filter = `blur(${blurRadius}px)`;
}
colorCtx.drawImage(originalImg, 0, 0, width, height);
// Get pixel data from the blurred image.
const imageData = colorCtx.getImageData(0, 0, width, height);
const data = imageData.data;
// Apply posterization to reduce the number of colors.
const levels = Math.max(2, Math.min(255, colorLevels));
const factor = 255 / (levels - 1);
for (let i = 0; i < data.length; i += 4) {
data[i] = Math.round(data[i] / factor) * factor; // Red
data[i + 1] = Math.round(data[i + 1] / factor) * factor; // Green
data[i + 2] = Math.round(data[i + 2] / factor) * factor; // Blue
}
colorCtx.putImageData(imageData, 0, 0);
// --- Step 2: Create the edge detection layer ---
const edgeCanvas = document.createElement('canvas');
edgeCanvas.width = width;
edgeCanvas.height = height;
const edgeCtx = edgeCanvas.getContext('2d', {
willReadFrequently: true
});
// Draw the original image and convert it to grayscale for edge detection.
edgeCtx.filter = 'grayscale(100%)';
edgeCtx.drawImage(originalImg, 0, 0, width, height);
const grayImageData = edgeCtx.getImageData(0, 0, width, height);
const grayData = grayImageData.data;
const edgeData = edgeCtx.createImageData(width, height);
const edgePixels = edgeData.data;
// Helper function to get the grayscale value of a pixel, handling image boundaries.
const getPixel = (x, y) => {
if (x < 0 || x >= width || y < 0 || y >= height) {
return 0; // Return black for out-of-bounds pixels
}
// Return the red channel value (since it's grayscale, R=G=B)
return grayData[(y * width + x) * 4];
};
// Sobel operator kernels for edge detection.
const kernelX = [
[-1, 0, 1],
[-2, 0, 2],
[-1, 0, 1]
];
const kernelY = [
[-1, -2, -1],
[0, 0, 0],
[1, 2, 1]
];
// Apply the Sobel filter to find edges.
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
let pixelX = 0;
let pixelY = 0;
for (let j = -1; j <= 1; j++) {
for (let i = -1; i <= 1; i++) {
const val = getPixel(x + i, y + j);
pixelX += val * kernelX[j + 1][i + 1];
pixelY += val * kernelY[j + 1][i + 1];
}
}
const magnitude = Math.sqrt(pixelX * pixelX + pixelY * pixelY);
const destIndex = (y * width + x) * 4;
if (magnitude > edgeThreshold) {
// This pixel is an edge, draw it with the specified edge color.
edgePixels[destIndex] = r;
edgePixels[destIndex + 1] = g;
edgePixels[destIndex + 2] = b;
edgePixels[destIndex + 3] = 255; // Opaque
} else {
// This pixel is not an edge, make it transparent.
edgePixels[destIndex + 3] = 0;
}
}
}
edgeCtx.putImageData(edgeData, 0, 0);
// --- Step 3: Combine the layers ---
// Draw the posterized color layer first.
ctx.drawImage(colorCanvas, 0, 0);
// Draw the detected edges on top.
ctx.drawImage(edgeCanvas, 0, 0);
return canvas;
}
Apply Changes