Please bookmark this page to avoid losing your image tool!

Image Old Soviet TV Video Filter With Artifacts And Synthwave Effects

(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 an old Soviet TV video filter with artifacts and synthwave effects to an image.
 * This function simulates CRT screen barrel distortion, RGB channel separation (chromatic aberration),
 * scanlines, noise, a vignette, and a colored glow on the highlights.
 *
 * @param {HTMLImageElement} originalImg The original javascript Image object.
 * @param {number} [scanlineIntensity=0.3] Controls the darkness of scanlines. Range 0 to 1.
 * @param {number} [vignetteIntensity=0.8] Controls the darkness of the corner vignette. Range 0 to 1.
 * @param {number} [noiseAmount=0.08] The amount of random static noise. Range 0 to 1.
 * @param {number} [distortionStrength=0.1] The strength of the CRT barrel distortion.
 * @param {number} [rgbShiftAmount=5] The amount of horizontal RGB channel separation in pixels.
 * @param {number} [glowIntensity=0.5] The brightness intensity of the synthwave glow.
 * @param {string} [glowColor='#00FFFF'] The color of the synthwave glow in hex format (e.g., '#FF00FF').
 * @returns {Promise<HTMLCanvasElement>} A promise that resolves to a new canvas element with the filter applied.
 */
async function processImage(originalImg, scanlineIntensity = 0.3, vignetteIntensity = 0.8, noiseAmount = 0.08, distortionStrength = 0.1, rgbShiftAmount = 5, glowIntensity = 0.5, glowColor = '#00FFFF') {

    // Helper to parse hex color string into an {r, g, b} object
    const parseHexColor = (hex) => {
        const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
        return result ? {
            r: parseInt(result[1], 16),
            g: parseInt(result[2], 16),
            b: parseInt(result[3], 16)
        } : null; // Return null if parse fails
    };

    const glowRgb = parseHexColor(glowColor);

    // Setup main canvas
    const canvas = document.createElement('canvas');
    // Using { willReadFrequently: true } is a performance hint for browsers
    const ctx = canvas.getContext('2d', {
        willReadFrequently: true
    });
    const w = originalImg.width;
    const h = originalImg.height;
    canvas.width = w;
    canvas.height = h;

    // Draw original image to a source canvas to securely get its pixel data
    const sourceCanvas = document.createElement('canvas');
    sourceCanvas.width = w;
    sourceCanvas.height = h;
    const sourceCtx = sourceCanvas.getContext('2d', {
        willReadFrequently: true
    });
    sourceCtx.drawImage(originalImg, 0, 0, w, h);
    const sourceData = sourceCtx.getImageData(0, 0, w, h);
    const sourcePixels = sourceData.data;

    // Prepare destination image data
    const destData = ctx.createImageData(w, h);
    const destPixels = destData.data;

    // Pre-calculate constants for the loop
    const cx = w / 2;
    const cy = h / 2;
    const maxRadius = Math.sqrt(cx * cx + cy * cy);
    const vignettePower = 2.5; // Creates a more realistic falloff for the vignette

    // A helper function for nearest-neighbor pixel sampling with boundary clamping
    const getPixel = (pixels, x, y) => {
        x = Math.round(x);
        y = Math.round(y);
        x = Math.max(0, Math.min(w - 1, x));
        y = Math.max(0, Math.min(h - 1, y));
        const i = (y * w + x) * 4;
        return [pixels[i], pixels[i + 1], pixels[i + 2], pixels[i + 3]];
    };

    // Main loop to process every pixel of the destination image
    for (let y = 0; y < h; y++) {
        for (let x = 0; x < w; x++) {
            const destIndex = (y * w + x) * 4;

            // --- 1. Barrel Distortion: Calculate source coordinates ---
            const dx = x - cx;
            const dy = y - cy;
            const r = Math.sqrt(dx * dx + dy * dy);
            // Calculate a bend factor based on distance from center
            const bend = distortionStrength * Math.pow(r / maxRadius, 2);
            // Apply the bend to find where to sample from the source image
            const sx = cx + dx * (1 - bend);
            const sy = cy + dy * (1 - bend);

            // --- 2. RGB Shift: Sample R, G, B channels from different locations ---
            const rPixel = getPixel(sourcePixels, sx + rgbShiftAmount, sy);
            const gPixel = getPixel(sourcePixels, sx, sy);
            const bPixel = getPixel(sourcePixels, sx - rgbShiftAmount, sy);

            let rVal = rPixel[0];
            let gVal = gPixel[1];
            let bVal = bPixel[2];
            let aVal = gPixel[3];

            // --- 3. Scanline Effect ---
            if (y % 3 === 0) {
                const factor = 1 - scanlineIntensity;
                rVal *= factor;
                gVal *= factor;
                bVal *= factor;
            }

            // --- 4. Noise Effect ---
            const noise = (Math.random() - 0.5) * 255 * noiseAmount;
            rVal += noise;
            gVal += noise;
            bVal += noise;

            // --- 5. Vignette Effect ---
            const vignette = Math.pow(Math.max(0, 1.0 - (r / maxRadius) * vignetteIntensity), vignettePower);
            rVal *= vignette;
            gVal *= vignette;
            bVal *= vignette;

            // --- Finalize: Clamp values and write to destination pixel ---
            destPixels[destIndex] = Math.max(0, Math.min(255, rVal));
            destPixels[destIndex + 1] = Math.max(0, Math.min(255, gVal));
            destPixels[destIndex + 2] = Math.max(0, Math.min(255, bVal));
            destPixels[destIndex + 3] = aVal;
        }
    }

    ctx.putImageData(destData, 0, 0);

    // --- 6. Synthwave Glow Effect (applied as a post-processing layer) ---
    if (glowIntensity > 0 && glowRgb) {
        // Create a highlight mask from the processed image
        const glowCanvas = document.createElement('canvas');
        glowCanvas.width = w;
        glowCanvas.height = h;
        const glowCtx = glowCanvas.getContext('2d', {
            willReadFrequently: true
        });
        glowCtx.putImageData(destData, 0, 0);

        const glowImageData = glowCtx.getImageData(0, 0, w, h);
        const glowPixels = glowImageData.data;

        for (let i = 0; i < glowPixels.length; i += 4) {
            // Use luminance to find bright areas
            const brightness = 0.2126 * glowPixels[i] + 0.7152 * glowPixels[i + 1] + 0.0722 * glowPixels[i + 2];
            if (brightness > 180) { // Threshold for what is considered a "highlight"
                glowPixels[i] = glowRgb.r;
                glowPixels[i + 1] = glowRgb.g;
                glowPixels[i + 2] = glowRgb.b;
            } else {
                glowPixels[i + 3] = 0; // Make non-highlight areas transparent
            }
        }
        glowCtx.putImageData(glowImageData, 0, 0);

        // Draw the blurred, glowing mask over the main image
        ctx.save();
        ctx.globalCompositeOperation = 'lighter';
        const blurAmount = Math.max(5, Math.floor(w / 60));
        ctx.filter = `blur(${blurAmount}px) brightness(${1.0 + glowIntensity})`;
        ctx.drawImage(glowCanvas, 0, 0);
        ctx.restore();
    }

    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 Old Soviet TV Video Filter with Artifacts and Synthwave Effects’ tool allows users to apply a nostalgic filter to images that simulates the look of old Soviet-era television screens. The tool features a variety of customizable effects, including barrel distortion, RGB channel separation (chromatic aberration), scanlines, noise, a vignette effect, and a synthwave glow. This can be particularly useful for creating retro-style artwork, enhancing digital media with a vintage aesthetic, or adding unique, stylized effects to photos for personal or commercial use. The tool is ideal for artists, graphic designers, or anyone looking to creatively manipulate images with a distinctive retro vibe.

Leave a Reply

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