Please bookmark this page to avoid losing your image tool!

Photo To Cinematic Look 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.
function processImage(originalImg, intensity = 100, addBlackBars = "yes", addVignette = "yes", addGrain = "yes") {
    // Parse strings and normalize intensity
    const strength = Math.max(0, (parseFloat(intensity) || 100) / 100);
    const bars = String(addBlackBars).toLowerCase().trim();
    const vignette = String(addVignette).toLowerCase().trim();
    const grain = String(addGrain).toLowerCase().trim();

    const canvas = document.createElement('canvas');
    const width = originalImg.naturalWidth || originalImg.width;
    const height = originalImg.naturalHeight || originalImg.height;
    
    if (!width || !height) return canvas;

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

    const ctx = canvas.getContext('2d');
    ctx.drawImage(originalImg, 0, 0, width, height);

    const imgData = ctx.getImageData(0, 0, width, height);
    const data = imgData.data;

    // Cinematic look involves applying an S-curve for contrast,
    // Teal & Orange split-toning, slight desaturation, lifting the blacks, and film grain.
    for (let i = 0; i < data.length; i += 4) {
        let a = data[i+3];
        // Skip fully transparent pixels
        if (a === 0) continue;

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

        // 1. Contrast boost via Luminance S-Curve
        let luma = 0.299 * r + 0.587 * g + 0.114 * b;
        let lumNorm = luma / 255;

        let contrastNorm = lumNorm < 0.5 
            ? 2 * lumNorm * lumNorm 
            : 1 - 2 * Math.pow(1 - lumNorm, 2);
        let contrastRatio = lumNorm > 0 ? contrastNorm / lumNorm : 1;
        
        let cStrength = 0.4 * strength; 
        let cr = r * (1 - cStrength) + (r * contrastRatio) * cStrength;
        let cg = g * (1 - cStrength) + (g * contrastRatio) * cStrength;
        let cb = b * (1 - cStrength) + (b * contrastRatio) * cStrength;

        // 2. Teal and Orange Split Toning Look
        let currentLuma = 0.299 * cr + 0.587 * cg + 0.114 * cb;
        let cLumNorm = Math.max(0, Math.min(1, currentLuma / 255));

        // Create fluid masks that peak in the shadows and highlights respectively.
        // Using optimized parabolas instead of slower trigonometric functions.
        let shadowMix = 0;
        let highlightMix = 0;
        if (cLumNorm < 0.5) {
            let x = cLumNorm / 0.5;
            shadowMix = 4 * x * (1 - x); // Peaks at 0.25
        } else {
            let x = (cLumNorm - 0.5) / 0.5;
            highlightMix = 4 * x * (1 - x); // Peaks at 0.75
        }

        // Apply distinct color shifts per tonal range
        let rShift = (highlightMix * 40 - shadowMix * 20) * strength;
        let gShift = (highlightMix * 10 + shadowMix * 10) * strength;
        let bShift = (shadowMix * 30 - highlightMix * 30) * strength;

        cr += rShift;
        cg += gShift;
        cb += bShift;

        // 3. Cinematic desaturation
        let finalLuma = 0.299 * cr + 0.587 * cg + 0.114 * cb;
        let satFactor = Math.max(0, 1.0 - (0.15 * strength));
        cr = finalLuma + (cr - finalLuma) * satFactor;
        cg = finalLuma + (cg - finalLuma) * satFactor;
        cb = finalLuma + (cb - finalLuma) * satFactor;

        // 4. Lift blacks for the classical "film fade" look
        let fadeLift = 12 * strength; 
        cr = fadeLift + cr * ((255 - fadeLift) / 255);
        cg = fadeLift + cg * ((255 - fadeLift) / 255);
        cb = fadeLift + cb * ((255 - fadeLift) / 255);

        // 5. Film Grain 
        if (grain === "yes" || grain === "true" || grain === "1") {
            let noise = (Math.random() - 0.5) * 20 * strength; 
            cr += noise;
            cg += noise;
            cb += noise;
        }

        // Output clamping correctly to 8-bit color space
        data[i]   = Math.min(255, Math.max(0, cr));
        data[i+1] = Math.min(255, Math.max(0, cg));
        data[i+2] = Math.min(255, Math.max(0, cb));
    }

    ctx.putImageData(imgData, 0, 0);

    // Make sure overlaid graphics only apply to the opaque parts of images with transparent backgrounds
    ctx.globalCompositeOperation = "source-atop";
    
    // Add visual vignette
    if (vignette === "yes" || vignette === "true" || vignette === "1") {
        let cx = width / 2;
        let cy = height / 2;
        let radius = Math.max(width, height) * 0.75;
        let gradient = ctx.createRadialGradient(cx, cy, Math.max(width, height) * 0.25, cx, cy, radius);
        gradient.addColorStop(0, "rgba(0,0,0,0)");
        gradient.addColorStop(1, `rgba(0,0,0,${Math.min(1, 0.5 * strength)})`);
        ctx.fillStyle = gradient;
        ctx.fillRect(0, 0, width, height);
    }

    // Add cinematic letterbox (Widescreen 2.35:1 visual simulation bounds)
    if (bars === "yes" || bars === "true" || bars === "1") {
        let barHeight = height * 0.12;
        ctx.fillStyle = "black";
        ctx.fillRect(0, 0, width, barHeight);
        ctx.fillRect(0, height - barHeight, width, barHeight);
    }
    
    // Reset blend mode just to be clean
    ctx.globalCompositeOperation = "source-over";

    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 photographs into images with a cinematic, film-like aesthetic. It applies professional color grading techniques such as teal and orange split-toning, contrast enhancement, and subtle desaturation. Users can further customize the look by adding film grain, a vignette effect, and cinematic black letterbox bars. This tool is ideal for photographers, content creators, or social media users looking to give their photos a dramatic, movie-inspired atmosphere.

Leave a Reply

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