Please bookmark this page to avoid losing your image tool!

Image Artistic Filter And Effect Grader

(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.
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;
}

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 Artistic Filter and Effect Grader is a versatile tool designed to transform your photos using a wide array of artistic filters and visual effects. It offers various modes ranging from classic looks like grayscale, sepia, and pencil sketches to creative styles such as dreamy blends, HDR enhancements, and painterly textures. Users can also apply specialized stylistic effects like 80s flash, night vision, blueprint, and pixelation. This tool is ideal for social media enthusiasts, digital artists, or anyone looking to quickly apply unique moods and creative compositions to their images.

Leave a Reply

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