Please bookmark this page to avoid losing your image tool!

VHSRip 16mm HIFI Stereo Photo Player

(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,
    rgbShiftStr = "4",
    noiseLevelStr = "35",
    scanlineIntensityStr = "0.2",
    showOSDStr = "yes",
    tint16mmStr = "yes"
) {
    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');
    
    // Set canvas dimensions to match the original image
    const w = originalImg.width;
    const h = originalImg.height;
    canvas.width = w;
    canvas.height = h;
    
    // Draw original image to canvas
    ctx.drawImage(originalImg, 0, 0, w, h);
    
    // Extract image data for pixel manipulation
    const imgData = ctx.getImageData(0, 0, w, h);
    const data = imgData.data;
    const outData = new Uint8ClampedArray(data.length);
    
    // Parse parameters
    const rgbShift = parseInt(rgbShiftStr, 10);
    const noiseLevel = parseFloat(noiseLevelStr);
    const scanlineIntensity = parseFloat(scanlineIntensityStr);
    const applyTint = (tint16mmStr.toLowerCase() === 'yes' || tint16mmStr === '1' || tint16mmStr.toLowerCase() === 'true');
    const showOSD = (showOSDStr.toLowerCase() === 'yes' || showOSDStr === '1' || showOSDStr.toLowerCase() === 'true');
    
    // Tracking band coordinates (simulating bottom VHS static)
    const trackingY1 = Math.floor(h * 0.90);
    const trackingY2 = Math.floor(h * 0.96);
    
    // Loop through each pixel
    for (let y = 0; y < h; y++) {
        let scanOffset = 0;
        
        // Simulating the distinct VHS tracking tear at the bottom
        if (y > trackingY1 && y < trackingY2) {
            scanOffset = Math.floor((Math.random() - 0.5) * (w * 0.03));
        } 
        // Simulating slight top tape tearing
        else if (y < h * 0.04) {
            scanOffset = Math.floor(Math.sin((y / (h * 0.04)) * Math.PI) * (w * 0.02) * Math.random());
        } 
        // Occasional random jitter for some scanlines
        else if (Math.random() < 0.015) {
            scanOffset = Math.floor((Math.random() - 0.5) * (w * 0.008));
        }

        const isScanline = (y % 3 === 0);
        const slMult = 1 - scanlineIntensity;
        const inTrackingBand = (y > trackingY1 && y < trackingY2);

        for (let x = 0; x < w; x++) {
            let srcX = x + scanOffset;
            
            // Boundary clamping for the offset
            if (srcX < 0) srcX = 0;
            if (srcX >= w) srcX = w - 1;

            // Calculate HIFI Stereo / Chromatic Aberration RGB shifts
            let rX = srcX - rgbShift;
            let bX = srcX + rgbShift;
            let gX = srcX;

            if (rX < 0) rX = 0; else if (rX >= w) rX = w - 1;
            if (bX < 0) bX = 0; else if (bX >= w) bX = w - 1;

            // Pixel indices
            const iOut = (y * w + x) * 4;
            const iR = (y * w + rX) * 4;
            const iG = (y * w + gX) * 4;
            const iB = (y * w + bX) * 4;

            let r = data[iR];
            let g = data[iG + 1];
            let b = data[iB + 2];

            // Add 16mm/VHS visual noise (Grain)
            if (noiseLevel > 0) {
                const noise = (Math.random() - 0.5) * noiseLevel;
                r += noise;
                g += noise;
                b += noise;
            }

            // Apply heavy white static snow inside the tracking band
            if (inTrackingBand && Math.random() < 0.35) {
                const snow = 120 + Math.random() * 135;
                r = snow;
                g = snow;
                b = snow;
            }

            // Apply Scanlines (darken every Nth horizontal line)
            if (isScanline) {
                r *= slMult;
                g *= slMult;
                b *= slMult;
            }

            // Simulate VHS color bleed by mixing original colors with Luma (desaturation + soften)
            const luma = 0.299 * r + 0.587 * g + 0.114 * b;
            r = r * 0.75 + luma * 0.25;
            g = g * 0.75 + luma * 0.25;
            b = b * 0.75 + luma * 0.25;

            // Optional 16mm Film Tint (Adds warmth to the picture)
            if (applyTint) {
                r += 12; // Enhance red
                g += 4;  // Slight green boost
                b -= 12; // Reduce blue
            }

            // Write processed values to outData
            outData[iOut] = r;
            outData[iOut + 1] = g;
            outData[iOut + 2] = b;
            outData[iOut + 3] = data[iOut + 3]; // Preserve original alpha
        }
    }
    
    // Update canvas with distorted pixel data
    imgData.data.set(outData);
    ctx.putImageData(imgData, 0, 0);

    // Apply Vintage Vignette overlay for 16mm lens feel
    const gradient = ctx.createRadialGradient(
        w / 2, h / 2, Math.min(w, h) * 0.4,
        w / 2, h / 2, Math.max(w, h) * 0.8
    );
    gradient.addColorStop(0, 'rgba(0, 0, 0, 0)');
    gradient.addColorStop(1, 'rgba(0, 0, 0, 0.65)');
    ctx.fillStyle = gradient;
    ctx.fillRect(0, 0, w, h);

    // Render VCR On-Screen Display (OSD)
    if (showOSD) {
        ctx.shadowColor = 'rgba(0, 0, 0, 0.9)';
        ctx.shadowBlur = Math.max(3, w * 0.005);
        ctx.shadowOffsetX = 2;
        ctx.shadowOffsetY = 2;
        ctx.fillStyle = '#f8f8ff'; // Slightly off-white

        // Base font size on image height
        const fontSize = Math.max(16, Math.floor(h * 0.05));
        ctx.font = `bold ${fontSize}px "Courier New", Courier, monospace`;

        const paddingX = w * 0.05;
        const paddingY = h * 0.08;

        // Top Left - PLAY
        ctx.textAlign = 'left';
        ctx.textBaseline = 'top';
        ctx.fillText('PLAY \u25BA', paddingX, paddingY);

        // Top Right - HIFI STEREO
        ctx.textAlign = 'right';
        ctx.fillText('HIFI STEREO', w - paddingX, paddingY);

        // Bottom Left - Speed Mode
        ctx.textAlign = 'left';
        ctx.textBaseline = 'bottom';
        ctx.fillText('SP', paddingX, h - paddingY);

        // Generate Pseudo-Recording Date & Time
        const d = new Date();
        const dateStr = `${d.getFullYear().toString().substr(-2)} ${String(d.getMonth() + 1).padStart(2,'0')} ${String(d.getDate()).padStart(2,'0')}`;
        
        let hours = d.getHours();
        const ampm = hours >= 12 ? 'PM' : 'AM';
        hours = hours % 12;
        hours = hours ? hours : 12; 
        const timeStr = `${hours}:${String(d.getMinutes()).padStart(2, '0')}:${String(d.getSeconds()).padStart(2, '0')} ${ampm}`;

        // Bottom Right - Timestamp
        ctx.textAlign = 'right';
        ctx.fillText(timeStr, w - paddingX, h - paddingY);
        ctx.fillText(dateStr, w - paddingX, h - paddingY - (fontSize * 1.2));
        
        // Reset shadows so they don't affect anything done later
        ctx.shadowColor = 'transparent';
        ctx.shadowBlur = 0;
        ctx.shadowOffsetX = 0;
        ctx.shadowOffsetY = 0;
    }

    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 VHSRip 16mm HIFI Stereo Photo Player is an image processing tool designed to apply vintage analog aesthetics to your digital photos. It simulates the visual characteristics of old media, such as VHS tapes and 16mm film, by introducing effects like RGB color shifts, film grain, scanlines, and tracking distortions. Users can also add an authentic on-screen display (OSD) featuring classic playback indicators and timestamps, as well as a warm color tint and vignette to mimic vintage camera lenses. This tool is ideal for creators looking to give their images a nostalgic, lo-fi, or retro cinematic look for social media, artistic projects, or themed digital content.

Leave a Reply

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