You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, posterizeLevels = 6, edgeThreshold = 55, auTintHex = "#7a42f4", tintIntensity = 0.15) {
// 1. Validate and prep the parameters
posterizeLevels = Math.max(2, parseInt(posterizeLevels, 10) || 6);
edgeThreshold = parseFloat(edgeThreshold) || 55;
tintIntensity = parseFloat(tintIntensity) || 0.15;
auTintHex = typeof auTintHex === 'string' ? auTintHex.trim() : "#7a42f4";
let tintR = 122, tintG = 66, tintB = 244; // Default "AU" purplish/stealth hue
if (auTintHex.startsWith('#') && auTintHex.length === 7) {
tintR = parseInt(auTintHex.substring(1, 3), 16);
tintG = parseInt(auTintHex.substring(3, 5), 16);
tintB = parseInt(auTintHex.substring(5, 7), 16);
}
const width = originalImg.width;
const height = originalImg.height;
// 2. Setup the canvas
const canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext('2d');
// Draw original image
ctx.drawImage(originalImg, 0, 0);
const imgData = ctx.getImageData(0, 0, width, height);
const data = imgData.data;
const outputData = new Uint8ClampedArray(data.length);
// Filter to simulate the transition from "3D Movie" to "2D The Series"
// We achieve this via Cel-shading: Posterization + Edge Detection + "AU" Color Tint
const grayscale = new Float32Array(width * height);
// Apply AU Tint and extract luma for edges
for (let i = 0; i < data.length; i += 4) {
// Overlay tint to give it that thematic Alternate Universe / The Series vibe
data[i] = data[i] * (1 - tintIntensity) + tintR * tintIntensity;
data[i+1] = data[i+1] * (1 - tintIntensity) + tintG * tintIntensity;
data[i+2] = data[i+2] * (1 - tintIntensity) + tintB * tintIntensity;
// Grayscale conversion (Luminosity method) for edge detection later
grayscale[i/4] = data[i] * 0.299 + data[i+1] * 0.587 + data[i+2] * 0.114;
}
// Sobel 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];
const levelStep = 255 / (posterizeLevels - 1);
const levelChunk = 256 / posterizeLevels;
// Apply Posterization and Edge Detection algorithms
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
const idx = (y * width + x) * 4;
// 1. Posterize (Reduces color palette to emulate 2D flat animation coloring)
outputData[idx] = Math.floor(data[idx] / levelChunk) * levelStep;
outputData[idx + 1] = Math.floor(data[idx+1] / levelChunk) * levelStep;
outputData[idx + 2] = Math.floor(data[idx+2] / levelChunk) * levelStep;
outputData[idx + 3] = data[idx + 3]; // Preserve Alpha
// 2. Edge Detection (Ignores 1px borders)
if (x > 0 && x < width - 1 && y > 0 && y < height - 1) {
let pixelX = 0;
let pixelY = 0;
let k = 0;
for (let j = -1; j <= 1; j++) {
for (let i = -1; i <= 1; i++) {
const grayIdx = ((y + j) * width + (x + i));
pixelX += grayscale[grayIdx] * kernelX[k];
pixelY += grayscale[grayIdx] * kernelY[k];
k++;
}
}
const magnitude = Math.sqrt(pixelX * pixelX + pixelY * pixelY);
// If edge detected, draw comic style line-art
if (magnitude > edgeThreshold) {
// Soft stylistic dark lines (instead of pure #000) for traditional animation look
outputData[idx] = outputData[idx] * 0.3;
outputData[idx+1] = outputData[idx+1] * 0.3;
outputData[idx+2] = outputData[idx+2] * 0.4;
}
}
}
}
// 3. Render back to canvas
ctx.putImageData(new ImageData(outputData, width, height), 0, 0);
return canvas;
}
Apply Changes