Please bookmark this page to avoid losing your image tool!

Photo To 1880s Film Style Converter With Customization Options

(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,
    tone = "sepia", // options: "sepia", "grayscale", "warm", "cyanotype"
    noiseLevel = 30, // 0 - 100
    scratchCount = 15, // 0 - 100
    vignetteStrength = 50, // 0 - 100
    blurAmount = 1, // 0 - 10
    contrast = -10, // -50 to 50
    brightness = 10, // -50 to 50
    addBorder = "yes", // "yes" or "no"
    dustLevel = 20 // 0 - 100
) {
    // Create a new canvas to process the image
    const canvas = document.createElement("canvas");
    const w = originalImg.width;
    const h = originalImg.height;
    canvas.width = w;
    canvas.height = h;
    const ctx = canvas.getContext("2d");

    // Calculate effect intensity factor (0 to 1)
    const intFactor = Math.max(0, Math.min(100, Number(intensity))) / 100;

    // If intensity is 0, just return the original image exactly as is
    if (intFactor === 0) {
        ctx.drawImage(originalImg, 0, 0);
        return canvas;
    }

    // 1. Initial Image Pass (Filters: Blur, Brightness, Contrast, and Base Tone)
    const blurVal = Math.max(0, Number(blurAmount)) * intFactor;
    const brVal = 100 + Number(brightness) * intFactor;
    const ctVal = 100 + Number(contrast) * intFactor;
    let filters = `blur(${blurVal}px) brightness(${brVal}%) contrast(${ctVal}%)`;

    const selectedTone = String(tone).toLowerCase();
    if (selectedTone === "sepia") {
        filters += ` sepia(${100 * intFactor}%)`;
    } else if (
        selectedTone === "grayscale" ||
        selectedTone === "cyanotype" ||
        selectedTone === "warm"
    ) {
        filters += ` grayscale(${100 * intFactor}%)`;
    }

    ctx.filter = filters;
    ctx.drawImage(originalImg, 0, 0, w, h);
    ctx.filter = "none";

    // 2. Specialized Tonal Tints (Cyanotype/Warm)
    if (selectedTone === "cyanotype") {
        // Cyanotype is a dark blue print
        ctx.globalCompositeOperation = "color";
        ctx.fillStyle = `rgba(15, 76, 129, ${intFactor})`;
        ctx.fillRect(0, 0, w, h);
        ctx.globalCompositeOperation = "overlay";
        ctx.fillStyle = `rgba(15, 76, 129, ${0.4 * intFactor})`;
        ctx.fillRect(0, 0, w, h);
        ctx.globalCompositeOperation = "source-over";
    } else if (selectedTone === "warm") {
        // Deep saddle brown tint over grayscale
        ctx.globalCompositeOperation = "color";
        ctx.fillStyle = `rgba(139, 90, 43, ${0.8 * intFactor})`;
        ctx.fillRect(0, 0, w, h);
        ctx.globalCompositeOperation = "source-over";
    }

    // 3. Vignette Effect (Darkened Corners)
    const parsedVignette = Number(vignetteStrength);
    if (parsedVignette > 0) {
        const cx = w / 2;
        const cy = h / 2;
        const radius = Math.sqrt(cx * cx + cy * cy);
        const gradient = ctx.createRadialGradient(cx, cy, radius * 0.3, cx, cy, radius);
        gradient.addColorStop(0, "rgba(0,0,0,0)");
        gradient.addColorStop(1, `rgba(0,0,0,${(parsedVignette / 100) * intFactor})`);

        ctx.globalCompositeOperation = "multiply";
        ctx.fillStyle = gradient;
        ctx.fillRect(0, 0, w, h);
        ctx.globalCompositeOperation = "source-over";
    }

    // 4. Draw Physical Damage (Film Scratches)
    const parsedScratches = Number(scratchCount);
    if (parsedScratches > 0) {
        const numScratches = Math.floor(parsedScratches * intFactor * (w / 800));
        for (let i = 0; i < numScratches; i++) {
            ctx.beginPath();
            const sx = Math.random() * w;
            const sy = Math.random() * h * 0.1;
            const ex = sx + (Math.random() * 60 - 30);
            const ey = h - Math.random() * h * 0.1;

            ctx.moveTo(sx, sy);
            ctx.lineTo(ex, ey);
            ctx.lineWidth = Math.random() * 1.5 + 0.5;

            // Randomize scratch color (light or dark)
            if (Math.random() > 0.4) {
                ctx.strokeStyle = `rgba(255, 255, 255, ${Math.random() * 0.4 * intFactor})`;
            } else {
                ctx.strokeStyle = `rgba(0, 0, 0, ${Math.random() * 0.4 * intFactor})`;
            }
            ctx.stroke();
        }
    }

    // 5. Draw Physical Damage (Dust and Specks)
    const parsedDust = Number(dustLevel);
    if (parsedDust > 0) {
        const numDust = Math.floor(parsedDust * intFactor * ((w * h) / 50000));
        for (let i = 0; i < numDust; i++) {
            const x = Math.random() * w;
            const y = Math.random() * h;
            const size = Math.random() * 2.5 + 0.5;

            ctx.beginPath();
            ctx.arc(x, y, size, 0, Math.PI * 2);
            if (Math.random() > 0.3) {
                ctx.fillStyle = `rgba(0,0,0,${Math.random() * 0.6 * intFactor})`;
            } else {
                ctx.fillStyle = `rgba(255,255,255,${Math.random() * 0.5 * intFactor})`;
            }
            ctx.fill();
        }
    }

    // 6. Draw 1880s Paper/Card Border Over the Edges
    if (String(addBorder).trim().toLowerCase() === "yes") {
        const bt = Math.max(12, w * 0.04); // Base thickness of the border

        ctx.fillStyle = `rgba(220, 210, 190, ${intFactor})`; // Pale aged paper color
        
        // Draw the 4 sides of the border over the photo
        ctx.fillRect(0, 0, w, bt); // Top
        ctx.fillRect(0, h - bt, w, bt); // Bottom
        ctx.fillRect(0, bt, bt, h - 2 * bt); // Left
        ctx.fillRect(w - bt, bt, bt, h - 2 * bt); // Right

        // Outer dark burnt edge
        ctx.strokeStyle = `rgba(0,0,0,${0.3 * intFactor})`;
        ctx.lineWidth = 1;
        ctx.strokeRect(0, 0, w, h);
        ctx.strokeRect(bt, bt, w - 2 * bt, h - 2 * bt);

        // Typical inset ornamental border line often found on old cabinet cards
        const inset = bt + w * 0.015;
        ctx.lineWidth = Math.max(1, w * 0.003);
        ctx.strokeStyle = `rgba(60, 40, 20, ${0.7 * intFactor})`;
        ctx.strokeRect(inset, inset, w - 2 * inset, h - 2 * inset);
    }

    // 7. Apply Film Grain (Noise) via direct pixel manipulation
    const parsedNoise = Number(noiseLevel);
    if (parsedNoise > 0) {
        const idata = ctx.getImageData(0, 0, w, h);
        const data = idata.data;
        const noiseAmt = parsedNoise * 1.2 * intFactor; // Scale noise to a reasonable 0-255 delta

        for (let i = 0; i < data.length; i += 4) {
            // Apply monochromatic grain only if the pixel isn't fully transparent
            if (data[i + 3] > 0) {
                const noise = (Math.random() - 0.5) * noiseAmt;
                data[i] += noise;     // Red
                data[i + 1] += noise; // Green
                data[i + 2] += noise; // Blue
            }
        }
        ctx.putImageData(idata, 0, 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

This tool transforms modern photographs into vintage-style images reminiscent of 1880s film and photography. Users can apply various tonal effects such as sepia, grayscale, warm tints, or cyanotype to achieve an antique look. The tool offers extensive customization options, allowing you to adjust intensity, noise level, film scratches, dust, vignette strength, blur, contrast, and brightness. Additionally, you can add aged paper borders to complete the historical aesthetic. This converter is ideal for creative photography projects, social media content with a nostalgic theme, or adding a sense of history to digital portraits.

Leave a Reply

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