Please bookmark this page to avoid losing your image tool!

Image Old Soviet TV Video Filter Creator

(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 to an image, simulating effects like
 * color degradation, scanlines, noise, vignetting, and CRT screen curvature.
 *
 * @param {Image} originalImg The original javascript Image object.
 * @param {number} desaturation=0.5 The amount of color desaturation (0 to 1). Default is 0.5.
 * @param {string} colorTint='#80a080' The hex color code for the color tint. Default is a greenish '#80a080'.
 * @param {number} tintIntensity=0.3 The intensity of the color tint (0 to 1). Default is 0.3.
 * @param {number} scanlineIntensity=0.3 The opacity of the scanlines (0 to 1). Default is 0.3.
 * @param {number} noiseIntensity=0.2 The intensity of the random noise (0 to 1). Default is 0.2.
 * @param {number} barrelDistortion=0.4 The amount of barrel distortion to simulate a curved screen (0 to 1). Default is 0.4.
 * @param {number} vignetteIntensity=0.7 The darkness of the vignette at the corners (0 to 1). Default is 0.7.
 * @returns {HTMLCanvasElement} A canvas element with the filtered image.
 */
function processImage(originalImg, desaturation = 0.5, colorTint = '#80a080', tintIntensity = 0.3, scanlineIntensity = 0.3, noiseIntensity = 0.2, barrelDistortion = 0.4, vignetteIntensity = 0.7) {
    const width = originalImg.naturalWidth;
    const height = originalImg.naturalHeight;

    const canvas = document.createElement('canvas');
    canvas.width = width;
    canvas.height = height;
    // Use willReadFrequently for performance gains on repeated getImageData calls
    const ctx = canvas.getContext('2d', {
        willReadFrequently: true
    });

    // Helper to parse hex color string
    const hexToRgb = (hex) => {
        if (!hex || typeof hex !== 'string') return null;
        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;
    };
    const tintRgb = hexToRgb(colorTint);

    // 1. Draw original image
    ctx.drawImage(originalImg, 0, 0, width, height);

    // 2. Apply pixel-level effects: Desaturation, Tint, and Noise
    if (desaturation > 0 || tintIntensity > 0 || noiseIntensity > 0) {
        const imageData = ctx.getImageData(0, 0, width, height);
        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 avg = 0.299 * r + 0.587 * g + 0.114 * b; // Luminosity
                r = r * (1 - desaturation) + avg * desaturation;
                g = g * (1 - desaturation) + avg * desaturation;
                b = b * (1 - desaturation) + avg * desaturation;
            }

            // Color Tint
            if (tintIntensity > 0 && tintRgb) {
                r = r * (1 - tintIntensity) + tintRgb.r * tintIntensity;
                g = g * (1 - tintIntensity) + tintRgb.g * tintIntensity;
                b = b * (1 - tintIntensity) + tintRgb.b * tintIntensity;
            }

            // Noise
            if (noiseIntensity > 0) {
                const noise = (Math.random() - 0.5) * 255 * noiseIntensity;
                r += noise;
                g += noise;
                b += noise;
            }

            // Clamp values
            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. Apply Barrel Distortion (CRT screen curve)
    if (barrelDistortion > 0) {
        const srcImageData = ctx.getImageData(0, 0, width, height);
        const srcData = srcImageData.data;
        const destImageData = ctx.createImageData(width, height);
        const destData = destImageData.data;

        const cx = width / 2;
        const cy = height / 2;
        const k = barrelDistortion / 2; // Distortion strength
        const maxR2 = cx * cx + cy * cy; // Approx. max squared radius

        for (let y = 0; y < height; y++) {
            for (let x = 0; x < width; x++) {
                const dx = x - cx;
                const dy = y - cy;
                const r2 = dx * dx + dy * dy;
                const factor = 1 + k * (r2 / maxR2);

                const sx = cx + dx / factor;
                const sy = cy + dy / factor;

                const destIndex = (y * width + x) * 4;

                if (sx >= 0 && sx < width && sy >= 0 && sy < height) {
                    // Use nearest-neighbor sampling
                    const srcX = Math.floor(sx);
                    const srcY = Math.floor(sy);
                    const srcIndex = (srcY * width + srcX) * 4;
                    destData[destIndex] = srcData[srcIndex];
                    destData[destIndex + 1] = srcData[srcIndex + 1];
                    destData[destIndex + 2] = srcData[srcIndex + 2];
                    destData[destIndex + 3] = srcData[srcIndex + 3];
                } else {
                    // Fill with black for pixels outside the source
                    destData[destIndex] = 0;
                    destData[destIndex + 1] = 0;
                    destData[destIndex + 2] = 0;
                    destData[destIndex + 3] = 255;
                }
            }
        }
        ctx.putImageData(destImageData, 0, 0);
    }

    // 4. Apply Overlay Effects: Scanlines and Vignette
    // Scanlines
    if (scanlineIntensity > 0) {
        ctx.fillStyle = `rgba(0, 0, 0, ${scanlineIntensity})`;
        for (let y = 0; y < height; y += 3) {
            ctx.fillRect(0, y, width, 1);
        }
    }

    // Vignette
    if (vignetteIntensity > 0) {
        const outerRadius = Math.sqrt(width * width + height * height) / 2;
        const gradient = ctx.createRadialGradient(width / 2, height / 2, 0, width / 2, height / 2, outerRadius);
        gradient.addColorStop(0.3, 'rgba(0,0,0,0)');
        gradient.addColorStop(1, `rgba(0,0,0,${vignetteIntensity})`);
        ctx.fillStyle = gradient;
        ctx.fillRect(0, 0, width, height);
    }

    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 Creator allows users to apply a vintage ‘Old Soviet TV’ effect to their images. This tool simulates various retro television characteristics such as color degradation, scanlines, noise, vignetting, and CRT screen curvature. Ideal for enhancing personal photos, designing nostalgic graphics, or creating artistic visuals, this tool is perfect for retro-themed projects or anyone looking to achieve a distinctive and stylized appearance reminiscent of old televised broadcasts.

Leave a Reply

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