Please bookmark this page to avoid losing your image tool!

Image To Home Video Style Camera Effect Converter

(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.
async function processImage(
    originalImg,
    effectToggles = "scanlines,noise,rgbShift,ui,grading",
    noiseIntensity = 30,
    rgbShiftAmount = 3,
    timestampText = "OCT 24 1995"
) {
    // 1. Setup the canvas
    const canvas = document.createElement('canvas');
    canvas.width = originalImg.width;
    canvas.height = originalImg.height;
    const ctx = canvas.getContext('2d');

    // Draw the initial image
    ctx.drawImage(originalImg, 0, 0);

    const width = canvas.width;
    const height = canvas.height;

    // 2. Extract image data for pixel manipulation
    const imgData = ctx.getImageData(0, 0, width, height);
    const data = imgData.data;
    
    // Create a copy to read un-mutated values for shifting effects
    const originalData = new Uint8ClampedArray(data);

    // Parsing toggles safely
    const effects = effectToggles.toLowerCase();
    const hasRgbShift = effects.includes('rgbshift');
    const hasNoise = effects.includes('noise');
    const hasGrading = effects.includes('grading');
    const hasScanlines = effects.includes('scanlines');
    const hasUI = effects.includes('ui');

    const shiftX = Math.floor(rgbShiftAmount);

    for (let y = 0; y < height; y++) {
        for (let x = 0; x < width; x++) {
            const i = (y * width + x) * 4;

            let r = originalData[i];
            let g = originalData[i + 1];
            let b = originalData[i + 2];

            // --- RGB Shift (Chromatic Aberration) ---
            if (hasRgbShift) {
                const shiftLeftX = Math.max(0, x - shiftX);
                const shiftRightX = Math.min(width - 1, x + shiftX);

                const shiftLeftIdx = (y * width + shiftLeftX) * 4;
                const shiftRightIdx = (y * width + shiftRightX) * 4;

                r = originalData[shiftLeftIdx];     // Red shifted from the left
                b = originalData[shiftRightIdx + 2];// Blue shifted from the right
                // Green stays the same
            }

            // --- Grading (Vintage, Faded, High-Contrast) ---
            if (hasGrading) {
                // Convert to luminance/grayscale for blending
                const gray = 0.299 * r + 0.587 * g + 0.114 * b;
                
                // Desaturate slightly (approximate 40% desaturation)
                const desaturation = 0.4;
                r = r + (gray - r) * desaturation;
                g = g + (gray - g) * desaturation;
                b = b + (gray - b) * desaturation;

                // Adjust Contrast (1.2) & Brightness (+15)
                const contrast = 1.2;
                const brightness = 15;
                r = Math.min(255, Math.max(0, ((r / 255 - 0.5) * contrast + 0.5) * 255 + brightness));
                g = Math.min(255, Math.max(0, ((g / 255 - 0.5) * contrast + 0.5) * 255 + brightness));
                b = Math.min(255, Math.max(0, ((b / 255 - 0.5) * contrast + 0.5) * 255 + brightness));

                // Mild Vignette Calculation
                const dx = x - width / 2;
                const dy = y - height / 2;
                const distance = Math.sqrt(dx * dx + dy * dy);
                const maxDistance = Math.sqrt((width / 2) ** 2 + (height / 2) ** 2);
                const vignetteEffect = Math.max(0, distance / maxDistance);
                
                // Deepen edges
                r -= vignetteEffect * 40;
                g -= vignetteEffect * 40;
                b -= vignetteEffect * 40;
            }

            // --- Film/VHS Noise ---
            if (hasNoise) {
                const noiseAmount = (Math.random() - 0.5) * noiseIntensity * 2;
                r = Math.min(255, Math.max(0, r + noiseAmount));
                g = Math.min(255, Math.max(0, g + noiseAmount));
                b = Math.min(255, Math.max(0, b + noiseAmount));
            }

            // Put processed pixel back
            data[i] = r;
            data[i + 1] = g;
            data[i + 2] = b;
            // alpha data[i+3] remains untouched
        }
    }

    // Apply pixel data back to canvas
    ctx.putImageData(imgData, 0, 0);

    // --- CRT/VHS Scanlines ---
    if (hasScanlines) {
        ctx.fillStyle = 'rgba(0, 0, 0, 0.12)';
        // Draw horizontal lines across the image
        const scanlineWidth = Math.max(2, Math.floor(height * 0.005));
        for (let y = 0; y < height; y += scanlineWidth * 2) {
            ctx.fillRect(0, y, width, scanlineWidth);
        }
    }

    // --- Home Video Style OSD UI ---
    if (hasUI) {
        // Load an appropriate retro font dynamically
        const fontName = 'VT323';
        if (!document.getElementById(`font-${fontName}`)) {
            const link = document.createElement('link');
            link.id = `font-${fontName}`;
            link.rel = 'stylesheet';
            link.href = `https://fonts.googleapis.com/css2?family=${fontName}&display=swap`;
            document.head.appendChild(link);
        }
        
        try {
            // Wait for the font to be ready to avoid default font flashes
            if (document.fonts && document.fonts.ready) {
                await document.fonts.load(`10pt "${fontName}"`);
                await document.fonts.ready;
            }
        } catch (e) {
            console.warn("Could not load Google Font. Falling back to system monospace.");
        }

        // Configure UI Font Size responsively
        const fontSize = Math.max(20, Math.floor(height * 0.05));
        ctx.font = `${fontSize}px "${fontName}", "Courier New", monospace`;
        
        // Add a classic shadow for the OSD feel
        ctx.shadowColor = 'black';
        ctx.shadowBlur = Math.max(2, fontSize * 0.1);
        ctx.shadowOffsetX = Math.max(1, fontSize * 0.05);
        ctx.shadowOffsetY = Math.max(1, fontSize * 0.05);
        
        const padding = fontSize;

        // 1. Draw "REC" Text
        ctx.fillStyle = '#ffffff';
        ctx.textBaseline = 'top';
        ctx.fillText('REC', padding, padding);

        // 2. Draw blinking Red Dot (Simulated state)
        // Calculating text width securely
        const recMetrics = ctx.measureText('REC  ');
        ctx.fillStyle = '#ff1111';
        ctx.fillText('●', padding + recMetrics.width, padding);

        // 3. Draw Battery Icon (Top Right)
        ctx.fillStyle = '#ffffff';
        ctx.strokeStyle = '#ffffff';
        
        const bW = fontSize * 2.5; 
        const bH = fontSize * 1.0;
        const bX = width - bW - padding * 1.5;
        const bY = padding;

        ctx.lineWidth = Math.max(2, Math.floor(fontSize * 0.08));
        
        // Battery Body
        ctx.strokeRect(bX, bY, bW, bH);
        // Battery Terminal (right stub)
        ctx.fillRect(bX + bW + 2, bY + (bH * 0.25), bW * 0.1, bH * 0.5);

        // Battery level bars (2 out of 3 blocks to look partially drained)
        const gap = bW * 0.1;
        const barW = (bW - (gap * 4)) / 3;
        ctx.fillRect(bX + gap, bY + gap, barW, bH - gap * 2);
        ctx.fillRect(bX + gap * 2 + barW, bY + gap, barW, bH - gap * 2);

        // 4. Draw Timestamp (Bottom Left)
        ctx.fillStyle = '#ffffff';
        ctx.textBaseline = 'bottom';
        // Add AM indicator line and then the customizable timestamp
        ctx.fillText('AM 12:00', padding, height - padding - (fontSize * 1.2));
        ctx.fillText(timestampText.toUpperCase(), padding, height - padding);
    }

    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

This tool transforms standard images into vintage home video footage by applying various retro aesthetic effects. It can simulate the look of old VHS tapes or camcorder recordings through features like RGB color shifting (chromatic aberration), film noise, scanlines, and color grading that adds desaturation and vignetting. Additionally, it can overlay a classic On-Screen Display (OSD) including a ‘REC’ indicator, a battery icon, and a customizable timestamp. This tool is ideal for creators looking to add a nostalgic or lo-fi aesthetic to their photos for social media, digital art projects, or themed graphic designs.

Leave a Reply

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