Please bookmark this page to avoid losing your image tool!

Image Retro TV Filter Effect Tool

(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,
    pixelationFactor = 3,
    desaturation = 0.3,
    sepiaStrength = 0.1,
    greenTintStrength = 0.1,
    noiseIntensity = 0.08,
    scanLineOpacity = 0.15,
    scanLineHeight = 1,
    vignetteStrength = 0.6
) {
    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');

    // Ensure image is loaded, otherwise width/height might be 0 or incorrect
    if (!originalImg.complete || typeof originalImg.naturalWidth === "undefined" || originalImg.naturalWidth === 0) {
        console.error("Image not loaded or invalid image provided to processImage.");
        // Return a placeholder canvas indicating an error
        canvas.width = 200;
        canvas.height = 150;
        ctx.fillStyle = '#333';
        ctx.fillRect(0, 0, canvas.width, canvas.height);
        ctx.fillStyle = 'white';
        ctx.font = '12px Arial';
        ctx.textAlign = 'center';
        ctx.fillText("Error: Image not loaded", canvas.width / 2, canvas.height / 2);
        return canvas;
    }

    const w = originalImg.width;
    const h = originalImg.height;
    canvas.width = w;
    canvas.height = h;

    // Validate and sanitize parameters
    pixelationFactor = Math.max(1, Math.floor(Number(pixelationFactor) || 1));
    desaturation = Math.max(0, Math.min(1, Number(desaturation) || 0));
    sepiaStrength = Math.max(0, Math.min(1, Number(sepiaStrength) || 0));
    greenTintStrength = Math.max(0, Math.min(1, Number(greenTintStrength) || 0));
    noiseIntensity = Math.max(0, Math.min(1, Number(noiseIntensity) || 0));
    scanLineOpacity = Math.max(0, Math.min(1, Number(scanLineOpacity) || 0));
    scanLineHeight = Math.max(1, Math.floor(Number(scanLineHeight) || 1));
    vignetteStrength = Math.max(0, Math.min(1, Number(vignetteStrength) || 0));

    // 1. Pixelation
    if (pixelationFactor > 1) {
        const pw = Math.max(1, Math.floor(w / pixelationFactor));
        const ph = Math.max(1, Math.floor(h / pixelationFactor));

        const tempCanvas = document.createElement('canvas');
        tempCanvas.width = pw;
        tempCanvas.height = ph;
        const tempCtx = tempCanvas.getContext('2d');
        
        // Draw original image scaled down to pixelated size
        tempCtx.drawImage(originalImg, 0, 0, pw, ph);

        // Disable smoothing when scaling up for pixelated effect
        ctx.imageSmoothingEnabled = false;
        // For wider compatibility (though modern browsers support unprefixed)
        // ctx.mozImageSmoothingEnabled = false; 
        // ctx.webkitImageSmoothingEnabled = false;
        // ctx.msImageSmoothingEnabled = false;
        
        ctx.drawImage(tempCanvas, 0, 0, w, h);
        
        // Re-enable smoothing for subsequent drawing operations (like vignette)
        ctx.imageSmoothingEnabled = true;
        // ctx.mozImageSmoothingEnabled = true;
        // ctx.webkitImageSmoothingEnabled = true;
        // ctx.msImageSmoothingEnabled = true;
    } else {
        ctx.drawImage(originalImg, 0, 0, w, h);
    }

    // 2. Image Data processing (Desaturation, Sepia, Green Tint, Noise)
    // Only get/put image data if any of these effects are active
    if (desaturation > 0 || sepiaStrength > 0 || greenTintStrength > 0 || noiseIntensity > 0) {
        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];
            let g = data[i + 1];
            let b = data[i + 2];

            // Desaturation
            if (desaturation > 0) {
                const gray = 0.299 * r + 0.587 * g + 0.114 * b;
                r = r * (1 - desaturation) + gray * desaturation;
                g = g * (1 - desaturation) + gray * desaturation;
                b = b * (1 - desaturation) + gray * desaturation;
            }

            // Sepia
            if (sepiaStrength > 0) {
                const sr = 0.393 * r + 0.769 * g + 0.189 * b;
                const sg = 0.349 * r + 0.686 * g + 0.168 * b;
                const sb = 0.272 * r + 0.534 * g + 0.131 * b;
                r = r * (1 - sepiaStrength) + sr * sepiaStrength;
                g = g * (1 - sepiaStrength) + sg * sepiaStrength;
                b = b * (1 - sepiaStrength) + sb * sepiaStrength;
            }

            // Green Tint (emulate CRT phosphors)
            if (greenTintStrength > 0) {
                const greenBoost = 30 * greenTintStrength; // Max boost of 30 to green
                const colorReduction = 15 * greenTintStrength; // Max reduction of 15 from r/b
                g += greenBoost;
                r -= colorReduction;
                b -= colorReduction;
            }
            
            // Noise (grayscale noise for a film grain/static look)
            if (noiseIntensity > 0) {
                // noiseMagnitude determines the max pixel value change due to noise.
                // E.g., if noiseIntensity is 0.1, noiseMagnitude is 5. Pixel values change by up to +/-5.
                const noiseMagnitude = 50 * noiseIntensity; 
                const noise = (Math.random() - 0.5) * 2 * noiseMagnitude; // Value between -noiseMagnitude and +noiseMagnitude
                r += noise;
                g += noise;
                b += noise;
            }
            
            // Clamp values and assign back
            data[i] = Math.max(0, Math.min(255, r));
            data[i + 1] = Math.max(0, Math.min(255, g));
            data[i + 2] = Math.max(0, Math.min(255, b));
        }
        ctx.putImageData(imageData, 0, 0);
    }

    // 3. Scan Lines
    if (scanLineOpacity > 0 && scanLineHeight > 0) {
        ctx.fillStyle = `rgba(0, 0, 0, ${scanLineOpacity})`;
        for (let y = 0; y < h; y += scanLineHeight * 2) { // Creates lines of scanLineHeight, with gaps of scanLineHeight
            ctx.fillRect(0, y, w, scanLineHeight);
        }
    }

    // 4. Vignette
    if (vignetteStrength > 0) {
        // Ensure smoothing is on for a nice gradient if it was turned off
        ctx.imageSmoothingEnabled = true;

        const centerX = w / 2;
        const centerY = h / 2;
        // Inner radius is where the vignette effect starts (fully transparent before this point)
        const innerRadius = Math.min(w, h) * 0.15; 
        // Outer radius is where the vignette is at its darkest (or full strength)
        const outerRadius = Math.max(w, h) * 0.7; 

        const gradient = ctx.createRadialGradient(centerX, centerY, innerRadius, centerX, centerY, outerRadius);
        gradient.addColorStop(0, 'rgba(0,0,0,0)'); // Center of vignette is transparent
        gradient.addColorStop(1, `rgba(0,0,0,${vignetteStrength})`); // Edge of vignette is dark
        
        ctx.fillStyle = gradient;
        ctx.fillRect(0, 0, w, h);
    }

    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 Retro TV Filter Effect Tool allows users to apply a vintage retro television effect to their images. This tool offers various customizable parameters such as pixelation, desaturation, sepia tone, green tint, noise, scan lines, and vignette strength, enabling users to simulate the nostalgic look of classic television screens. It can be used for personal projects, social media posts, or artistic endeavors where a retro aesthetic is desired, providing an easy way to enhance images with a unique visual style.

Leave a Reply

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