Please bookmark this page to avoid losing your image tool!

Movie Image Viewer

(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, filmType = "motion", filter = "cinematic", text = "КИНО") {
    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');
    
    // Determine layout: "motion" (movie film) has vertical layout with side holes
    // "still" (photo film) has horizontal layout with top/bottom holes
    const isMotion = filmType.toLowerCase() !== "still";
    const W = originalImg.width;
    const H = originalImg.height;
    
    // Base margins on the smallest dimension to ensure proportionality
    const minDim = Math.min(W, H);
    const margin = minDim * 0.15;
    const frameGap = minDim * 0.05;
    
    // Canvas dimensions
    const cWidth = isMotion ? W + 2 * margin : W + 2 * frameGap;
    const cHeight = isMotion ? H + 2 * frameGap : H + 2 * margin;
    
    canvas.width = cWidth;
    canvas.height = cHeight;
    
    // Fill the film strip background with very dark gray (acting as the unexposed film strip)
    ctx.fillStyle = '#080808'; 
    ctx.fillRect(0, 0, cWidth, cHeight);
    
    // Determine image placement
    const imgX = isMotion ? margin : frameGap;
    const imgY = isMotion ? frameGap : margin;
    
    // Apply CSS filters if requested
    if (filter === "grayscale") {
        ctx.filter = "grayscale(100%) contrast(1.1)";
    } else if (filter === "cinematic") {
        // Dramatic cinematic color grade: higher contrast, desaturated, slight sepia warming
        ctx.filter = "contrast(1.2) brightness(0.95) saturate(0.85) sepia(0.2)";
    } else {
        ctx.filter = "none";
    }
    
    // Draw the image onto the "frame"
    ctx.drawImage(originalImg, imgX, imgY, W, H);
    ctx.filter = "none";
    
    // Apply film/cinematic visual overlays over the image
    if (filter === "cinematic") {
        // 1. Widescreen Letterboxing (Anamorphic Look ~ 2.35:1)
        const targetRatio = 2.35;
        const imgRatio = W / H;
        
        ctx.fillStyle = '#080808';
        if (imgRatio < targetRatio - 0.02) {
            // Taller image: Add black bars on top and bottom inside the frame
            const visibleHeight = W / targetRatio;
            const letterboxHeight = (H - visibleHeight) / 2;
            ctx.fillRect(imgX, imgY, W, letterboxHeight);
            ctx.fillRect(imgX, imgY + H - letterboxHeight, W, letterboxHeight);
        } else if (imgRatio > targetRatio + 0.02) {
            // Wider image: Add black bars on sides inside the frame
            const visibleWidth = H * targetRatio;
            const letterboxWidth = (W - visibleWidth) / 2;
            ctx.fillRect(imgX, imgY, letterboxWidth, H);
            ctx.fillRect(imgX + W - letterboxWidth, imgY, letterboxWidth, H);
        }
        
        // 2. Cinematic Vignette (darken corners)
        const cx = imgX + W / 2;
        const cy = imgY + H / 2;
        const grad = ctx.createRadialGradient(cx, cy, minDim * 0.4, cx, cy, Math.max(W, H) * 0.8);
        grad.addColorStop(0, 'rgba(0,0,0,0)');
        grad.addColorStop(1, 'rgba(0,0,0,0.7)');
        ctx.fillStyle = grad;
        ctx.fillRect(imgX, imgY, W, H);
        
        // 3. Film Grain Emulation (create slightly translucent noise)
        const grainCanvas = document.createElement('canvas');
        const gW = Math.min(W, 800);
        const gH = Math.min(H, 800);
        grainCanvas.width = gW;
        grainCanvas.height = gH;
        const gCtx = grainCanvas.getContext('2d');
        const imgData = gCtx.createImageData(gW, gH);
        const data = imgData.data;
        for (let i = 0; i < data.length; i += 4) {
             const val = (Math.random() * 255) | 0;
             data[i] = val;     // R
             data[i+1] = val;   // G
             data[i+2] = val;   // B
             data[i+3] = 20;    // A (Low opacity)
        }
        gCtx.putImageData(imgData, 0, 0);
        ctx.globalCompositeOperation = 'overlay';
        ctx.drawImage(grainCanvas, 0, 0, gW, gH, imgX, imgY, W, H);
        ctx.globalCompositeOperation = 'source-over';

        // 4. Subtle Film Scratch (vertical line)
        ctx.fillStyle = 'rgba(255, 255, 255, 0.06)';
        const scratchX = imgX + W * 0.3 + Math.random() * (W * 0.4);
        ctx.fillRect(scratchX, imgY, 1 + Math.random() * 2, H);
    }
    
    // --- Film Strip Perforations (Holes) ---
    // Punch holes using 'destination-out' to make them transparent
    ctx.globalCompositeOperation = 'destination-out';
    ctx.fillStyle = '#000';
    
    // Perforations physical geometry mimicking actual 35mm film stock
    const hWidth = isMotion ? margin * 0.4 : margin * 0.2;
    const hHeight = isMotion ? margin * 0.2 : margin * 0.4;
    const radius = Math.min(hWidth, hHeight) * 0.2;
    
    function roundRect(ctx, x, y, width, height, radius) {
        ctx.beginPath();
        ctx.moveTo(x + radius, y);
        ctx.lineTo(x + width - radius, y);
        ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
        ctx.lineTo(x + width, y + height - radius);
        ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
        ctx.lineTo(x + radius, y + height);
        ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
        ctx.lineTo(x, y + radius);
        ctx.quadraticCurveTo(x, y, x + radius, y);
        ctx.closePath();
        ctx.fill();
    }
    
    if (isMotion) {
        // Vertical layout: holes on left and right borders 
        const numHoles = Math.max(4, Math.floor(cHeight / (margin * 0.6)));
        const pitch = cHeight / numHoles; 
        for (let i = 0; i < numHoles; i++) {
            const cy = pitch * (i + 0.5);
            const hY = cy - hHeight / 2;
            const hXLeft = margin / 2 - hWidth / 2;
            const hXRight = cWidth - margin / 2 - hWidth / 2;
            
            roundRect(ctx, hXLeft, hY, hWidth, hHeight, radius);
            roundRect(ctx, hXRight, hY, hWidth, hHeight, radius);
        }
    } else {
        // Horizontal Layout: holes on top and bottom borders
        const numHoles = Math.max(8, Math.floor(cWidth / (margin * 0.6)));
        const pitch = cWidth / numHoles;
        for (let i = 0; i < numHoles; i++) {
            const cx = pitch * (i + 0.5);
            const hX = cx - hWidth / 2;
            const hYTop = margin / 2 - hHeight / 2;
            const hYBottom = cHeight - margin / 2 - hHeight / 2;
            
            roundRect(ctx, hX, hYTop, hWidth, hHeight, radius);
            roundRect(ctx, hX, hYBottom, hWidth, hHeight, radius);
        }
    }
    
    ctx.globalCompositeOperation = 'source-over';
    
    // --- Typography & Edge Codes ---
    ctx.fillStyle = '#db9c30'; // Kodak golden color
    ctx.font = `bold ${Math.max(10, margin * 0.15)}px Courier New, monospace`;
    ctx.textAlign = 'center';
    ctx.textBaseline = 'middle';
    
    if (isMotion) {
        // Left text margin
        ctx.save();
        ctx.translate(margin * 0.15, cHeight / 2);
        ctx.rotate(-Math.PI / 2);
        ctx.fillText(text, 0, 0);
        ctx.restore();
        
        // Right text margin
        ctx.save();
        ctx.translate(cWidth - margin * 0.15, cHeight / 2);
        ctx.rotate(Math.PI / 2);
        ctx.fillText("SAFETY FILM ▼", 0, 0);
        ctx.restore();
    } else {
        // Top text margin
        ctx.fillText(text, cWidth / 2, margin * 0.15);
        // Bottom text margin
        ctx.fillText("SAFETY FILM ▼", cWidth / 2, cHeight - margin * 0.15);
    }
    
    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 Movie Image Viewer is an image processing tool that transforms standard photos into stylized film strips. Users can choose between two distinct layouts: a vertical ‘motion’ film style or a horizontal ‘still’ photo film style, complete with realistic perforations (holes) along the edges. The tool offers various cinematic filters, including grayscale and a dramatic cinematic mode that adds widescreen letterboxing, vignette effects, film grain, and subtle scratches to emulate authentic analog film. This tool is ideal for photographers, content creators, and designers looking to add a vintage or cinematic aesthetic to their images for social media, presentations, or creative projects.

Leave a Reply

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