Please bookmark this page to avoid losing your image tool!

Image Video Effects Toolkit

(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.
/**
 * Applies various video-like effects to a static image.
 *
 * @param {Image} originalImg The original source image object.
 * @param {string} effectName The name of the effect to apply. Available options: 'glitch', 'noise', 'scanlines', 'oldfilm', 'rgbsplit', 'pixelate', 'invert', 'sepia'. Defaults to 'glitch'.
 * @param {number} intensity A value between 0.0 and 1.0 that controls the strength of the effect. Defaults to 0.5.
 * @returns {HTMLCanvasElement} A new canvas element with the applied effect.
 */
function processImage(originalImg, effectName = 'glitch', intensity = 0.5) {
    const canvas = document.createElement('canvas');
    // Using willReadFrequently is a performance hint for browsers for frequent getImageData calls.
    const ctx = canvas.getContext('2d', {
        willReadFrequently: true
    });
    const w = originalImg.naturalWidth;
    const h = originalImg.naturalHeight;
    canvas.width = w;
    canvas.height = h;

    // Clamp intensity to a valid 0.0 - 1.0 range
    intensity = Math.max(0, Math.min(1, intensity));

    // For most effects, we draw the image first. Pixelate is an exception.
    if (effectName.toLowerCase() !== 'pixelate') {
        ctx.drawImage(originalImg, 0, 0, w, h);
    }

    switch (effectName.toLowerCase()) {
        case 'glitch':
            {
                if (intensity === 0) break;
                const imageData = ctx.getImageData(0, 0, w, h);
                const data = imageData.data;
                const originalData = new Uint8ClampedArray(data);

                for (let y = 0; y < h; y++) {
                    // Introduce a random chance to start a glitch strip
                    if (Math.random() < intensity * 0.05) {
                        const blockHeight = Math.floor(Math.random() * h * 0.1) + 1;
                        const xOffset = Math.floor((Math.random() - 0.5) * w * 0.2 * intensity);

                        for (let j = y; j < y + blockHeight && j < h; j++) {
                            for (let x = 0; x < w; x++) {
                                const sourceIndex = (j * w + x) * 4;
                                let targetX = x + xOffset;

                                if (targetX >= 0 && targetX < w) {
                                    const targetIndex = (j * w + targetX) * 4;
                                    data[targetIndex] = originalData[sourceIndex];
                                    data[targetIndex + 1] = originalData[sourceIndex + 1];
                                    data[targetIndex + 2] = originalData[sourceIndex + 2];
                                }
                            }
                        }
                        y += blockHeight; // Skip past the glitched block
                    }
                }
                ctx.putImageData(imageData, 0, 0);
                break;
            }

        case 'noise':
            {
                if (intensity === 0) break;
                const imageData = ctx.getImageData(0, 0, w, h);
                const data = imageData.data;
                const amount = 255 * intensity * 0.2; // Max 20% noise
                for (let i = 0; i < data.length; i += 4) {
                    const random = (Math.random() - 0.5) * amount;
                    data[i] += random; // red
                    data[i + 1] += random; // green
                    data[i + 2] += random; // blue
                }
                ctx.putImageData(imageData, 0, 0);
                break;
            }

        case 'scanlines':
            {
                if (intensity === 0) break;
                const lineHeight = Math.max(1, Math.floor(intensity * 5));
                ctx.fillStyle = `rgba(0, 0, 0, ${intensity * 0.2})`;
                for (let y = 0; y < h; y += lineHeight * 2) {
                    ctx.fillRect(0, y, w, lineHeight);
                }
                break;
            }

        case 'oldfilm':
            {
                if (intensity === 0) break;
                // 1. Sepia & Grain
                const imageData = ctx.getImageData(0, 0, w, h);
                const data = imageData.data;
                for (let i = 0; i < data.length; i += 4) {
                    let r = data[i], g = data[i + 1], b = data[i + 2];
                    // Sepia calculation
                    let tr = 0.393 * r + 0.769 * g + 0.189 * b;
                    let tg = 0.349 * r + 0.686 * g + 0.168 * b;
                    let tb = 0.272 * r + 0.534 * g + 0.131 * b;
                    // Blend with original based on intensity
                    data[i] = r * (1 - intensity) + tr * intensity;
                    data[i + 1] = g * (1 - intensity) + tg * intensity;
                    data[i + 2] = b * (1 - intensity) + tb * intensity;
                    // Grain
                    const noise = (Math.random() - 0.5) * 60 * intensity;
                    data[i] += noise;
                    data[i + 1] += noise;
                    data[i + 2] += noise;
                }
                ctx.putImageData(imageData, 0, 0);

                // 2. Scratches
                const numScratches = Math.floor(w * 0.05 * intensity);
                for (let i = 0; i < numScratches; i++) {
                    const x = Math.random() * w;
                    ctx.strokeStyle = `rgba(255, 255, 255, ${Math.random() * 0.4 * intensity})`;
                    ctx.lineWidth = Math.random() * 2 + 0.5;
                    ctx.beginPath();
                    ctx.moveTo(x, 0);
                    ctx.lineTo(x + (Math.random() - 0.5) * 10, h);
                    ctx.stroke();
                }

                // 3. Vignette
                const outerRadius = Math.sqrt(w * w + h * h) / 2;
                const gradient = ctx.createRadialGradient(w / 2, h / 2, outerRadius * (1 - intensity * 0.9), w / 2, h / 2, outerRadius);
                gradient.addColorStop(0, 'rgba(0,0,0,0)');
                gradient.addColorStop(1, `rgba(0,0,0,${0.7 * intensity})`);
                ctx.fillStyle = gradient;
                ctx.fillRect(0, 0, w, h);
                break;
            }

        case 'rgbsplit':
            {
                if (intensity === 0) break;
                const imageData = ctx.getImageData(0, 0, w, h);
                const originalData = new Uint8ClampedArray(imageData.data);
                const data = imageData.data;
                const offset = Math.floor(w * 0.015 * intensity);

                for (let i = 0; i < data.length; i += 4) {
                    const x = (i / 4) % w;
                    const y = Math.floor((i / 4) / w);
                    const rIndex = (y * w + Math.max(0, x - offset)) * 4;
                    const bIndex = (y * w + Math.min(w - 1, x + offset)) * 4;
                    data[i] = originalData[rIndex]; // Red from left
                    data[i + 2] = originalData[bIndex + 2]; // Blue from right
                }
                ctx.putImageData(imageData, 0, 0);
                break;
            }

        case 'pixelate':
            {
                // Intensity 0 -> 1px (no change), Intensity 1 -> ~20px
                const pixelSize = Math.max(1, Math.round(intensity * 20));
                if (pixelSize <= 1) {
                    ctx.drawImage(originalImg, 0, 0, w, h);
                    break;
                };

                const smallW = Math.ceil(w / pixelSize);
                const smallH = Math.ceil(h / pixelSize);
                
                // Draw original image scaled down into a temporary canvas
                const tempCanvas = document.createElement('canvas');
                tempCanvas.width = smallW;
                tempCanvas.height = smallH;
                const tempCtx = tempCanvas.getContext('2d');
                tempCtx.drawImage(originalImg, 0, 0, smallW, smallH);

                // Scale temp canvas back up to original size with smoothing disabled
                ctx.imageSmoothingEnabled = false;
                ctx.drawImage(tempCanvas, 0, 0, smallW, smallH, 0, 0, w, h);
                break;
            }

        case 'invert':
            {
                if (intensity === 0) break;
                const imageData = ctx.getImageData(0, 0, w, h);
                const data = imageData.data;
                for (let i = 0; i < data.length; i += 4) {
                    const r = data[i], g = data[i + 1], b = data[i + 2];
                    data[i] = r * (1 - intensity) + (255 - r) * intensity;
                    data[i + 1] = g * (1 - intensity) + (255 - g) * intensity;
                    data[i + 2] = b * (1 - intensity) + (255 - b) * intensity;
                }
                ctx.putImageData(imageData, 0, 0);
                break;
            }

        case 'sepia':
            {
                if (intensity === 0) break;
                const imageData = ctx.getImageData(0, 0, w, h);
                const data = imageData.data;
                for (let i = 0; i < data.length; i += 4) {
                    const r = data[i], g = data[i + 1], b = data[i + 2];
                    const tr = 0.393 * r + 0.769 * g + 0.189 * b;
                    const tg = 0.349 * r + 0.686 * g + 0.168 * b;
                    const tb = 0.272 * r + 0.534 * g + 0.131 * b;
                    // Blend with original color based on intensity
                    data[i] = r * (1 - intensity) + tr * intensity;
                    data[i + 1] = g * (1 - intensity) + tg * intensity;
                    data[i + 2] = b * (1 - intensity) + tb * intensity;
                }
                ctx.putImageData(imageData, 0, 0);
                break;
            }

        default:
            // If effectName is unknown, the original image is already on the canvas.
            break;
    }

    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 Video Effects Toolkit allows users to enhance static images by applying various video-like effects. This tool offers effects such as glitch, noise, scanlines, old film, RGB split, pixelate, invert, and sepia, each adjustable in intensity. It can be used for artistic photo editing, enhancing social media posts, creating unique visuals for presentations, or producing retro-style imagery reminiscent of classic films and digital artifacts.

Leave a Reply

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