You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, effect = "Autumn's Glow") {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const w = originalImg.width;
const h = originalImg.height;
canvas.width = w;
canvas.height = h;
const mode = effect.trim().toLowerCase();
// 1. Handled via Canvas native filters / composites for optimal performance
if (mode === 'grayscale') {
ctx.filter = 'grayscale(100%)';
ctx.drawImage(originalImg, 0, 0);
return canvas;
}
if (mode === 'retro sepia tone') {
ctx.filter = 'sepia(100%)';
ctx.drawImage(originalImg, 0, 0);
return canvas;
}
if (mode === 'pencil sketch') {
ctx.filter = 'grayscale(100%)';
ctx.drawImage(originalImg, 0, 0);
const baseGrayscale = ctx.getImageData(0, 0, w, h).data;
ctx.filter = 'grayscale(100%) invert(100%) blur(5px)';
ctx.drawImage(originalImg, 0, 0);
const blurredInvert = ctx.getImageData(0, 0, w, h);
const blendData = blurredInvert.data;
for (let i = 0; i < blendData.length; i += 4) {
let base = baseGrayscale[i];
let blend = blendData[i];
let val = blend === 255 ? 255 : Math.min(255, (base * 255) / (255 - blend));
blendData[i] = blendData[i + 1] = blendData[i + 2] = val;
blendData[i + 3] = 255;
}
ctx.putImageData(blurredInvert, 0, 0);
return canvas;
}
if (mode === 'dreamy blend') {
ctx.drawImage(originalImg, 0, 0);
const baseC = ctx.getImageData(0, 0, w, h).data;
ctx.filter = 'blur(10px)';
ctx.drawImage(originalImg, 0, 0);
const blurImgData = ctx.getImageData(0, 0, w, h);
const blurC = blurImgData.data;
for (let i = 0; i < blurC.length; i += 4) {
blurC[i] = 255 - (((255 - baseC[i]) * (255 - blurC[i])) >> 8);
blurC[i + 1] = 255 - (((255 - baseC[i + 1]) * (255 - blurC[i + 1])) >> 8);
blurC[i + 2] = 255 - (((255 - baseC[i + 2]) * (255 - blurC[i + 2])) >> 8);
blurC[i + 3] = 255;
}
ctx.putImageData(blurImgData, 0, 0);
return canvas;
}
if (mode === 'hdr photo') {
ctx.drawImage(originalImg, 0, 0);
const baseC = ctx.getImageData(0, 0, w, h).data;
ctx.filter = 'blur(6px)';
ctx.drawImage(originalImg, 0, 0);
const blurImgData = ctx.getImageData(0, 0, w, h);
const outData = blurImgData.data;
const clamp = (v) => Math.max(0, Math.min(255, v));
for (let i = 0; i < outData.length; i += 4) {
let r = baseC[i] + (baseC[i] - outData[i]) * 0.5;
let g = baseC[i + 1] + (baseC[i + 1] - outData[i + 1]) * 0.5;
let b = baseC[i + 2] + (baseC[i + 2] - outData[i + 2]) * 0.5;
outData[i] = clamp(r * 1.1 - 5);
outData[i + 1] = clamp(g * 1.1 - 5);
outData[i + 2] = clamp(b * 1.1 - 5);
outData[i + 3] = 255;
}
ctx.putImageData(blurImgData, 0, 0);
return canvas;
}
// 2. Pixel Iteration Effects
ctx.filter = 'none';
ctx.drawImage(originalImg, 0, 0);
const imgData = ctx.getImageData(0, 0, w, h);
const data = imgData.data;
const clamp = (v) => Math.max(0, Math.min(255, v));
const luma = (r, g, b) => 0.299 * r + 0.587 * g + 0.114 * b;
const getL = (x, y, src) => {
let cx = Math.max(0, Math.min(w - 1, x));
let cy = Math.max(0, Math.min(h - 1, y));
let i = (cy * w + cx) * 4;
return 0.299 * src[i] + 0.587 * src[i + 1] + 0.114 * src[i + 2];
};
switch (mode) {
case 'crystal': {
const sz = 16;
const temp = new Uint8ClampedArray(data);
for (let y = 0; y < h; y += sz) {
for (let x = 0; x < w; x += sz) {
let rSum = 0, gSum = 0, bSum = 0, count = 0;
for (let dy = 0; dy < sz && y + dy < h; dy++) {
for (let dx = 0; dx < sz && x + dx < w; dx++) {
let i = ((y + dy) * w + (x + dx)) * 4;
rSum += temp[i]; gSum += temp[i + 1]; bSum += temp[i + 2];
count++;
}
}
let r = rSum / count, g = gSum / count, b = bSum / count;
for (let dy = 0; dy < sz && y + dy < h; dy++) {
for (let dx = 0; dx < sz && x + dx < w; dx++) {
let i = ((y + dy) * w + (x + dx)) * 4;
let mod = (dx > dy) ? 20 : ((dx < dy) ? -20 : 0);
data[i] = clamp(r + mod);
data[i + 1] = clamp(g + mod);
data[i + 2] = clamp(b + mod);
}
}
}
}
break;
}
case 'contrast': {
const f = (259 * (100 + 255)) / (255 * (259 - 100));
for (let i = 0; i < data.length; i += 4) {
data[i] = clamp(f * (data[i] - 128) + 128);
data[i + 1] = clamp(f * (data[i + 1] - 128) + 128);
data[i + 2] = clamp(f * (data[i + 2] - 128) + 128);
}
break;
}
case 'soft amber': {
for (let i = 0; i < data.length; i += 4) {
data[i] = clamp(data[i] * 1.1 + 20);
data[i + 1] = clamp(data[i + 1] * 1.05 + 10);
data[i + 2] = clamp(data[i + 2] * 0.8);
}
break;
}
case '80s flash': {
for (let i = 0; i < data.length; i += 4) {
let r = data[i], g = data[i + 1], b = data[i + 2];
let l = luma(r, g, b);
data[i] = clamp(l + (r - l) * 1.5 + 20);
data[i + 1] = clamp(l + (g - l) * 1.5 + 20);
data[i + 2] = clamp(l + (b - l) * 1.5 + 20);
}
break;
}
case "autumn's glow": {
for (let i = 0; i < data.length; i += 4) {
let r = data[i], g = data[i + 1], b = data[i + 2];
if (g > b && g > r * 0.7) {
r = clamp(r + (g - r) * 0.6);
}
data[i] = clamp(r * 1.1 + 10);
data[i + 1] = clamp(g * 0.95);
data[i + 2] = clamp(b * 0.8);
}
break;
}
case "winter's breeze": {
for (let i = 0; i < data.length; i += 4) {
data[i] = clamp(data[i] * 0.85);
data[i + 1] = clamp(data[i + 1] * 0.95 + 10);
data[i + 2] = clamp(data[i + 2] * 1.2 + 20);
}
break;
}
case 'film grader': {
for (let i = 0; i < data.length; i += 4) {
let r = data[i], g = data[i + 1], b = data[i + 2];
let l = luma(r, g, b);
data[i] = clamp(r + (l / 255) * 40 - ((255 - l) / 255) * 10);
data[i + 1] = clamp(g + (l / 255) * 10 + ((255 - l) / 255) * 20);
data[i + 2] = clamp(b - (l / 255) * 30 + ((255 - l) / 255) * 40);
}
break;
}
case 'grain & texture': {
for (let i = 0; i < data.length; i += 4) {
let noise = (Math.random() - 0.5) * 60;
data[i] = clamp(data[i] + noise);
data[i + 1] = clamp(data[i + 1] + noise);
data[i + 2] = clamp(data[i + 2] + noise);
}
break;
}
case 'color swap': {
for (let i = 0; i < data.length; i += 4) {
let t = data[i];
data[i] = data[i + 1];
data[i + 1] = data[i + 2];
data[i + 2] = t;
}
break;
}
case 'selective color': {
for (let i = 0; i < data.length; i += 4) {
let r = data[i], g = data[i + 1], b = data[i + 2];
let isRed = (r > g * 1.5 && r > b * 1.5 && r > 80);
if (!isRed) {
data[i] = data[i + 1] = data[i + 2] = luma(r, g, b);
}
}
break;
}
case 'painting': {
const R = 2;
const qSize = (R + 1) * (R + 1);
const paintTemp = new Uint8ClampedArray(data);
const quadrants = [
{ dx: -R, dy: -R }, { dx: 0, dy: -R },
{ dx: -R, dy: 0 }, { dx: 0, dy: 0 }
];
for (let py = 0; py < h; py++) {
for (let px = 0; px < w; px++) {
let minVar = Infinity;
let bestColor = [0, 0, 0];
for (let q = 0; q < 4; q++) {
let rSum = 0, gSum = 0, bSum = 0;
let rSq = 0, gSq = 0, bSq = 0;
for (let iy = 0; iy <= R; iy++) {
for (let ix = 0; ix <= R; ix++) {
let cx = Math.max(0, Math.min(w - 1, px + quadrants[q].dx + ix));
let cy = Math.max(0, Math.min(h - 1, py + quadrants[q].dy + iy));
let idx = (cy * w + cx) * 4;
let r = paintTemp[idx], g = paintTemp[idx + 1], b = paintTemp[idx + 2];
rSum += r; gSum += g; bSum += b;
rSq += r * r; gSq += g * g; bSq += b * b;
}
}
let meanR = rSum / qSize, meanG = gSum / qSize, meanB = bSum / qSize;
let varR = rSq / qSize - meanR * meanR;
let varG = gSq / qSize - meanG * meanG;
let varB = bSq / qSize - meanB * meanB;
let variance = varR + varG + varB;
if (variance < minVar) {
minVar = variance;
bestColor = [meanR, meanG, meanB];
}
}
let i = (py * w + px) * 4;
data[i] = bestColor[0]; data[i + 1] = bestColor[1]; data[i + 2] = bestColor[2];
}
}
break;
}
case 'pixelate': {
const pSz = 10;
const pTemp = new Uint8ClampedArray(data);
for (let y = 0; y < h; y += pSz) {
for (let x = 0; x < w; x += pSz) {
let rSum = 0, gSum = 0, bSum = 0, count = 0;
for (let dy = 0; dy < pSz && y + dy < h; dy++) {
for (let dx = 0; dx < pSz && x + dx < w; dx++) {
let i = ((y + dy) * w + (x + dx)) * 4;
rSum += pTemp[i]; gSum += pTemp[i + 1]; bSum += pTemp[i + 2]; count++;
}
}
let r = rSum / count, g = gSum / count, b = bSum / count;
for (let dy = 0; dy < pSz && y + dy < h; dy++) {
for (let dx = 0; dx < pSz && x + dx < w; dx++) {
let idx = ((y + dy) * w + (x + dx)) * 4;
data[idx] = r; data[idx + 1] = g; data[idx + 2] = b;
}
}
}
}
break;
}
case 'ritone':
case 'tritone': {
let sC = [26, 11, 46], mC = [245, 92, 122], hC = [255, 235, 128];
for (let i = 0; i < data.length; i += 4) {
let l = luma(data[i], data[i + 1], data[i + 2]);
if (l < 128) {
let p = l / 128;
data[i] = sC[0] + p * (mC[0] - sC[0]);
data[i + 1] = sC[1] + p * (mC[1] - sC[1]);
data[i + 2] = sC[2] + p * (mC[2] - sC[2]);
} else {
let p = (l - 128) / 127;
data[i] = mC[0] + p * (hC[0] - mC[0]);
data[i + 1] = mC[1] + p * (hC[1] - mC[1]);
data[i + 2] = mC[2] + p * (hC[2] - mC[2]);
}
}
break;
}
case 'heatmap': {
const stops = [
{ v: 0, c: [0, 0, 0] }, { v: 51, c: [0, 0, 255] },
{ v: 102, c: [0, 255, 0] }, { v: 153, c: [255, 255, 0] },
{ v: 204, c: [255, 0, 0] }, { v: 255, c: [255, 255, 255] }
];
for (let i = 0; i < data.length; i += 4) {
let l = clamp(luma(data[i], data[i + 1], data[i + 2]));
let s1 = stops[0], s2 = stops[stops.length - 1];
for (let j = 0; j < stops.length - 1; j++) {
if (l >= stops[j].v && l <= stops[j + 1].v) {
s1 = stops[j]; s2 = stops[j + 1]; break;
}
}
let p = (l - s1.v) / (s2.v - s1.v || 1);
data[i] = s1.c[0] + p * (s2.c[0] - s1.c[0]);
data[i + 1] = s1.c[1] + p * (s2.c[1] - s1.c[1]);
data[i + 2] = s1.c[2] + p * (s2.c[2] - s1.c[2]);
}
break;
}
case 'nightvision': {
for (let i = 0; i < data.length; i += 4) {
let l = luma(data[i], data[i + 1], data[i + 2]);
let noise = (Math.random() - 0.5) * 40;
data[i] = 0;
data[i + 1] = clamp(l * 1.4 + noise);
data[i + 2] = 0;
}
break;
}
case 'ghost': {
for (let i = 0; i < data.length; i += 4) {
let l = luma(data[i], data[i + 1], data[i + 2]);
data[i] = clamp(120 + l * 0.4);
data[i + 1] = clamp(140 + l * 0.4);
data[i + 2] = clamp(160 + l * 0.5);
}
break;
}
case 'sobel edges':
case 'blueprint': {
const edgeTemp = new Uint8ClampedArray(data);
for (let y = 0; y < h; y++) {
for (let x = 0; x < w; x++) {
let gx = getL(x - 1, y - 1, edgeTemp) * -1 + getL(x + 1, y - 1, edgeTemp) * 1
+ getL(x - 1, y, edgeTemp) * -2 + getL(x + 1, y, edgeTemp) * 2
+ getL(x - 1, y + 1, edgeTemp) * -1 + getL(x + 1, y + 1, edgeTemp) * 1;
let gy = getL(x - 1, y - 1, edgeTemp) * -1 + getL(x, y - 1, edgeTemp) * -2 + getL(x + 1, y - 1, edgeTemp) * -1
+ getL(x - 1, y + 1, edgeTemp) * 1 + getL(x, y + 1, edgeTemp) * 2 + getL(x + 1, y + 1, edgeTemp) * 1;
let mag = clamp(Math.sqrt(gx * gx + gy * gy));
let i = (y * w + x) * 4;
if (mode === 'blueprint') {
let mix = mag / 255;
data[i] = clamp(51 * mix);
data[i + 1] = clamp(51 * (1 - mix) + 204 * mix);
data[i + 2] = clamp(102 * (1 - mix) + 255 * mix);
} else {
data[i] = data[i + 1] = data[i + 2] = mag;
}
}
}
break;
}
}
ctx.putImageData(imgData, 0, 0);
// 3. Post-Process Rendering Composites
ctx.globalCompositeOperation = 'source-over';
if (mode === '80s flash' || mode === 'nightvision') {
const rad = Math.max(w, h) * 0.7;
const grad = ctx.createRadialGradient(w / 2, h / 2, rad * 0.3, w / 2, h / 2, rad);
if (mode === '80s flash') {
grad.addColorStop(0, 'rgba(255, 255, 255, 0.1)');
grad.addColorStop(1, 'rgba(0, 0, 0, 0.6)');
} else {
grad.addColorStop(0, 'rgba(0, 0, 0, 0)');
grad.addColorStop(1, 'rgba(0, 0, 0, 0.85)');
}
ctx.fillStyle = grad;
ctx.fillRect(0, 0, w, h);
} else if (mode === 'soft amber') {
ctx.fillStyle = 'rgba(255, 160, 0, 0.15)';
ctx.globalCompositeOperation = 'color';
ctx.fillRect(0, 0, w, h);
} else if (mode === 'ghost') {
ctx.globalCompositeOperation = 'screen';
ctx.filter = 'blur(8px)';
ctx.globalAlpha = 0.4;
ctx.drawImage(canvas, 0, 0);
ctx.globalAlpha = 1.0;
}
return canvas;
}
Apply Changes