Please bookmark this page to avoid losing your image tool!

Photo To Realistic CCTV Surveillance Footage 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, 
    cameraID = "CAM 01", 
    timestamp = "auto", 
    noiseAmount = 25, 
    distortion = 0.35, 
    compression = 0.25
) {
    return new Promise((resolve) => {
        // Output resolution: 720p HD surveillance footage
        const width = 1280;
        const height = 720;

        // 1. Prepare offscreen canvas to scale and crop the original image
        const offCanvas = document.createElement('canvas');
        offCanvas.width = width;
        offCanvas.height = height;
        const offCtx = offCanvas.getContext('2d');

        // Apply a slight greenish/desaturated filter simulating cheap sensor
        offCtx.filter = 'saturate(0.55) contrast(1.15) brightness(0.95)';
        
        // Calculate cover scaling, but slightly zoomed out (scale * 0.8) to simulate 
        // distance and leave room for strong wide-angle barrel distortion
        const rawScale = Math.max(width / originalImg.width, height / originalImg.height);
        const scale = rawScale * 0.85; 
        const w = originalImg.width * scale;
        const h = originalImg.height * scale;
        const x = (width - w) / 2;
        const y = (height - h) / 2;

        // Fill background with black to act as the camera barrel edge
        offCtx.fillStyle = 'black';
        offCtx.fillRect(0, 0, width, height);

        // Draw image multiple times to simulate slight motion blur
        offCtx.globalAlpha = 0.3;
        offCtx.drawImage(originalImg, x - 3, y, w, h);
        offCtx.drawImage(originalImg, x, y, w, h);
        offCtx.globalAlpha = 0.4;
        offCtx.drawImage(originalImg, x + 3, y, w, h);
        offCtx.globalAlpha = 1.0;
        
        // Extract pixel data to apply lens distortion and noise
        const srcData = offCtx.getImageData(0, 0, width, height);
        const sD = srcData.data;

        // Create new canvas for distorted output
        const outCanvas = document.createElement('canvas');
        outCanvas.width = width;
        outCanvas.height = height;
        const outCtx = outCanvas.getContext('2d');
        const outImageData = outCtx.createImageData(width, height);
        const oD = outImageData.data;

        const cx = width / 2;
        const cy = height / 2;
        const icx = 1 / cx;
        const icy = 1 / cy;

        // 2. Pixel Manipulation Loop: Perspective, Barrel Distortion, Chromatic Aberration, Noise
        for (let py = 0; py < height; py++) {
            const yn = (py - cy) * icy;
            const pyw = py * width;

            for (let px = 0; px < width; px++) {
                const xn = (px - cx) * icx;

                // Perspective shift: simulate camera mounted high pointing slightly down
                // The top (yn < 0) is shrunk, pushing subjects back and widening the bottom.
                const perspectiveTilt = 1.0 - 0.15 * yn; 
                const adjXn = xn * perspectiveTilt; 
                const adjYn = yn * (1.0 - 0.05 * yn);

                // Barrel distortion (Fisheye wide-angle effect)
                const r2 = adjXn * adjXn + adjYn * adjYn;
                const distOffset = 1 + distortion * r2;
                
                const srcX = adjXn * distOffset;
                const srcY = adjYn * distOffset;

                // Chromatic Aberration: sample R, G, B channels at slightly different scales
                const rX = srcX * 0.988 * cx + cx;
                const gX = srcX * 1.000 * cx + cx;
                const bX = srcX * 1.012 * cx + cx;
                const sY = srcY * cy + cy;

                const outIdx = (pyw + px) * 4;

                if (gX >= 0 && gX < width && sY >= 0 && sY < height) {
                    const syIdx = (sY | 0) * width;
                    
                    const sxR = Math.max(0, Math.min(width - 1, rX | 0));
                    const sxG = gX | 0;
                    const sxB = Math.max(0, Math.min(width - 1, bX | 0));

                    const idxR = (syIdx + sxR) * 4;
                    const idxG = (syIdx + sxG) * 4;
                    const idxB = (syIdx + sxB) * 4;

                    const r = sD[idxR];
                    const g = sD[idxG + 1];
                    const b = sD[idxB + 2];

                    // Mild Sensor Noise
                    const noise = (Math.random() - 0.5) * noiseAmount;

                    oD[outIdx]     = r + noise;
                    oD[outIdx + 1] = g + noise;
                    oD[outIdx + 2] = b + noise;
                    oD[outIdx + 3] = 255;
                } else {
                    oD[outIdx + 3] = 255; // Leave as black outside the view
                }
            }
        }
        outCtx.putImageData(outImageData, 0, 0);

        // 3. Post-processing Overlays: Vignette, Lighting, Interlacing, UI
        // Radial Vignette
        const gradient = outCtx.createRadialGradient(cx, cy, height * 0.3, cx, cy, width * 0.7);
        gradient.addColorStop(0, 'rgba(0, 0, 0, 0)');
        gradient.addColorStop(1, 'rgba(0, 0, 0, 0.85)');
        outCtx.fillStyle = gradient;
        outCtx.fillRect(0, 0, width, height);

        // Subtile dark green hardware cast
        outCtx.fillStyle = 'rgba(20, 35, 25, 0.15)';
        outCtx.fillRect(0, 0, width, height);

        // Scanlines / Interlacing artifacts
        outCtx.fillStyle = 'rgba(0, 0, 0, 0.1)';
        for (let i = 0; i < height; i += 4) {
            outCtx.fillRect(0, i, width, 1);
        }

        // Draw CCTV Interface
        outCtx.font = 'bold 26px "Courier New", Courier, monospace';
        outCtx.textBaseline = 'top';
        const pad = 35;

        // Format Timestamp
        let dateStr = timestamp;
        if (dateStr === "auto") {
            const now = new Date();
            const p = n => n.toString().padStart(2, '0');
            dateStr = `${p(now.getDate())}-${p(now.getMonth()+1)}-${now.getFullYear()} ${p(now.getHours())}:${p(now.getMinutes())}:${p(now.getSeconds())}`;
        }

        // Text Styles
        outCtx.fillStyle = 'rgba(255, 255, 255, 0.9)';
        outCtx.shadowColor = 'black';
        outCtx.shadowBlur = 4;
        outCtx.shadowOffsetX = 2;
        outCtx.shadowOffsetY = 2;

        // Top Right: Timestamp
        const tsWidth = outCtx.measureText(dateStr).width;
        outCtx.fillText(dateStr, width - pad - tsWidth, pad);

        // Bottom Left: Camera ID
        outCtx.textBaseline = 'bottom';
        outCtx.fillText(cameraID, pad, height - pad);

        // Top Left: REC
        outCtx.textBaseline = 'top';
        outCtx.fillText("REC", pad + 25, pad);

        outCtx.shadowColor = 'transparent';
        outCtx.shadowOffsetX = 0;
        outCtx.shadowOffsetY = 0;
        outCtx.fillStyle = 'rgba(255, 0, 0, 0.85)';
        outCtx.beginPath();
        outCtx.arc(pad + 10, pad + 14, 6, 0, Math.PI * 2);
        outCtx.fill();

        // 4. Digital Compression: Simulate H.264/H.265 stream artifacts via low quality JPEG
        // This naturally creates the blocky compression seen in security footage
        const compressedDataUrl = outCanvas.toDataURL('image/jpeg', compression);

        const finalImg = new Image();
        finalImg.onload = () => {
            const finalCanvas = document.createElement('canvas');
            finalCanvas.width = width;
            finalCanvas.height = height;
            const finalCtx = finalCanvas.getContext('2d');
            
            // Add a very slight blur to soften the hard digital edges mimicking a cheap lens
            finalCtx.filter = 'blur(0.5px)';
            finalCtx.drawImage(finalImg, 0, 0);

            resolve(finalCanvas);
        };
        finalImg.src = compressedDataUrl;
    });
}

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 photos into realistic-looking CCTV surveillance footage. It applies a variety of visual effects to simulate a security camera, including wide-angle barrel distortion, chromatic aberration, sensor noise, and scanlines. The converter also adds authentic UI overlays such as a ‘REC’ indicator, a customizable camera ID, and a real-time timestamp. This tool is ideal for filmmakers, content creators, or pranksters looking to add a sense of security footage realism to their digital media and creative projects.

Leave a Reply

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