Please bookmark this page to avoid losing your image tool!

Image Holga Camera Filter Effect Tool

(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, vignetteStrength = 0.7, vignetteFalloff = 2.5, contrastValue = 1.4, saturationValue = 0.7, tintColor = "rgba(30,0,50,0.1)", grainAmount = 25, blurAmount = 0.5) {
    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d', { willReadFrequently: true }); // Optimization for frequent getImageData

    // Ensure the image is loaded, otherwise width/height might be 0
    const width = originalImg.naturalWidth || originalImg.width;
    const height = originalImg.naturalHeight || originalImg.height;

    if (width === 0 || height === 0) {
        console.error("Image not loaded or has zero dimensions.");
        // Return a minimal canvas to avoid breaking downstream expectations
        canvas.width = 1; 
        canvas.height = 1;
        return canvas;
    }

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

    // 1. Draw original image to canvas
    ctx.drawImage(originalImg, 0, 0, width, height);

    // 2. Apply blur (if any)
    // The blur filter applies to subsequent drawing operations.
    // So, we draw the image (now on the canvas) back onto itself with the filter active.
    if (blurAmount > 0) {
        ctx.filter = `blur(${blurAmount}px)`;
        ctx.drawImage(canvas, 0, 0, width, height); 
        ctx.filter = 'none'; // Reset filter for subsequent direct pixel manipulations and drawing
    }

    // 3. Get image data for pixel manipulation
    const imageData = ctx.getImageData(0, 0, width, height);
    const data = imageData.data;
    
    const centerX = width / 2;
    const centerY = height / 2;
    // maxDist is the distance from center to a corner, used for normalizing vignette distance
    const maxDist = Math.sqrt(centerX * centerX + centerY * centerY);

    for (let i = 0; i < data.length; i += 4) {
        let r = data[i];
        let g = data[i + 1];
        let b = data[i + 2];

        // 4a. Apply Contrast
        // Formula: NewValue = (((OldValue/255 - 0.5) * Contrast) + 0.5) * 255
        // This maps [0, 255] to [-0.5, 0.5], scales, then maps back.
        if (contrastValue !== 1.0) { // Optimization: skip if no change
            r = ( ( (r / 255 - 0.5) * contrastValue) + 0.5) * 255;
            g = ( ( (g / 255 - 0.5) * contrastValue) + 0.5) * 255;
            b = ( ( (b / 255 - 0.5) * contrastValue) + 0.5) * 255;
        }
        
        // 4b. Apply Saturation
        // Formula: NewValue = Luma + SaturationFactor * (OldValue - Luma)
        // SaturationFactor = 1.0 means no change. 0.0 means grayscale. <1.0 desaturates.
        if (saturationValue !== 1.0) { // Optimization: skip if no change
            // Standard luma coefficients (Rec. 601)
            const luma = r * 0.299 + g * 0.587 + b * 0.114;
            r = luma + saturationValue * (r - luma);
            g = luma + saturationValue * (g - luma);
            b = luma + saturationValue * (b - luma);
        }

        // 4c. Apply Vignette
        if (vignetteStrength > 0 && maxDist > 0) { // Optimization & prevent div by zero if maxDist is 0
            const x = (i / 4) % width; // Current pixel's x coordinate
            const y = Math.floor((i / 4) / width); // Current pixel's y coordinate
            
            const dx = x - centerX;
            const dy = y - centerY;
            const dist = Math.sqrt(dx * dx + dy * dy);
            
            // normDist will be 0 at center, 1 at maxDist (corners)
            const normDist = dist / maxDist; 
            
            // Vignette effect calculation:
            // Math.pow(normDist, vignetteFalloff) controls the curve of the vignette.
            // Higher vignetteFalloff means the vignette is more concentrated at the very edges.
            // vignetteStrength scales the overall darkness of the vignette.
            const darkRatio = Math.pow(normDist, vignetteFalloff);
            const vignetteEffect = 1.0 - (vignetteStrength * darkRatio);
            
            r *= vignetteEffect;
            g *= vignetteEffect;
            b *= vignetteEffect;
        }

        // 4d. Apply Film Grain
        if (grainAmount > 0) { // Optimization: skip if no change
            // Generates a random value in [-grainAmount/2, grainAmount/2]
            const grain = (Math.random() - 0.5) * grainAmount; 
            r += grain;
            g += grain;
            b += grain;
        }

        // Clamp values to the valid 0-255 range
        data[i] = Math.max(0, Math.min(255, r));
        data[i + 1] = Math.max(0, Math.min(255, g));
        data[i + 2] = Math.max(0, Math.min(255, b));
        // Alpha channel (data[i+3]) is preserved
    }

    // 5. Put modified image data back to the canvas
    ctx.putImageData(imageData, 0, 0);

    // 6. Apply tintColor overlay
    // Check if tintColor is a valid string and not "none" (case-insensitive) or empty
    if (tintColor && typeof tintColor === 'string' && tintColor.toLowerCase() !== 'none' && tintColor.trim() !== "") {
        // Browsers are generally lenient with invalid fillStyle values (often defaulting to black or transparent).
        // No complex validation is strictly needed for fillStyle itself, but checking the intent.
        ctx.fillStyle = tintColor;
        // Default globalCompositeOperation is 'source-over', which is mixing based on alpha of fillStyle.
        // Other operations like 'multiply', 'overlay', 'color' could give different Holga-like tint effects.
        // For this implementation, standard alpha blending ('source-over') is used.
        ctx.fillRect(0, 0, width, height);
    }
    
    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 Image Holga Camera Filter Effect Tool allows users to apply a vintage Holga camera effect to their images, enhancing them with characteristics such as vignette, contrast, saturation, and a film grain effect. This tool is useful for photographers and graphic designers who want to achieve a retro aesthetic or add artistic flair to their images. It can be used for creative projects, social media posts, or any scenario where users want to transform their photos into unique, stylistic representations.

Leave a Reply

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

Other Image Tools:

Image Fisheye Lens Distortion Filter Effect Tool

Image Kodak Gold 200 Film Filter Effect Tool

Photo Macro Filter Effect Tool

Image Neutral Density Filter Effect Tool

Image Green Filter Black And White Effect Tool

Image Large Format Film Filter Effect Creator

Image Pinhole Camera Filter Effect Tool

Image Warming Filter Effect Tool

Image Fujifilm Pro 400H Filter Effect Application

Image Diffusion Filter Effect Tool

Image Push-Processed Film Filter Effect Tool

Image Color Temperature Orange Filter Effect Tool

Image Kodak Ektar 100 Film Filter Effect

Image Yellow Filter Black And White Effect Tool

Image Expired Film Filter Effect Tool

Image Circular Polarizer Filter Effect Tool

Image Lomography Purple Filter Effect Tool

Image Split Field Filter Effect Tool

Image Soft Focus Filter Effect Tool

Image Medium Format Film Filter Effect

Image Wide-Angle Lens Perspective Filter Effect Tool

Olympus OM-1 Photo Filter Effect Tool

Image Fujifilm Velvia Filter Effect Tool

Image Lensbaby Selective Focus Filter Effect Tool

Image Color Temperature Blue Filter Effect Tool

Image UV Filter Effect Tool

Image Red Filter Black And White Effect Tool

Image Redscale Film Filter Effect

Image Cinestill 800T Filter Effect Tool

Image Glimmer Glass Filter Effect Tool

Image Star Filter Effect Tool

Image Kodak Portra 400 Film Filter Effect

Image Fujifilm Superia Filter Effect Tool

Image Tilt-Shift Lens Filter Effect Tool

Image Graduated Neutral Density Filter Effect Tool

Image Diana Camera Filter Effect Tool

See All →