Please bookmark this page to avoid losing your image tool!

Whimsical Photo Filter

(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,
    saturation = 1.4,      // Example: 0 (grayscale) to 2+ (amped saturation), 1 is original
    blurPx = 0.5,          // Example: 0 (no blur) to N pixels for Gaussian blur
    brightness = 1.05,     // Example: 0 (black) to 2+ (amped brightness), 1 is original
    contrast = 1.05,       // Example: 0 (gray) to 2+ (amped contrast), 1 is original
    tintColorStr = "255,200,150", // RGB string for tint, e.g., "255,200,150" for warm peach
    tintOpacity = 0.15,    // 0 (no tint) to 1 (full tint layer opacity)
    vignetteColorStr = "40,20,10", // RGB string for vignette, e.g., "40,20,10" for dark warm brown
    vignetteStrength = 0.6 // 0 (no vignette) to 1 (strongest vignette: color at full opacity from image center)
) {
    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d', { alpha: true }); // Ensure alpha for transparency

    const imgWidth = originalImg.naturalWidth || originalImg.width;
    const imgHeight = originalImg.naturalHeight || originalImg.height;

    if (imgWidth === 0 || imgHeight === 0) {
        console.error("Image has zero dimensions. Ensure the image is loaded and valid.");
        // Return a small, transparent canvas as a fallback
        canvas.width = 1; 
        canvas.height = 1;
        ctx.clearRect(0,0,1,1);
        return canvas; 
    }

    canvas.width = imgWidth;
    canvas.height = imgHeight;

    // 1. Apply base image filters (saturation, blur, brightness, contrast) and draw the image
    let filterString = '';
    // Saturate: values < 1 desaturate, 1 is original, > 1 supersaturates
    if (saturation !== 1) filterString += `saturate(${Math.max(0, saturation)}) `;
    // Blur: px value for Gaussian blur. Must be non-negative.
    if (blurPx > 0) filterString += `blur(${Math.max(0, blurPx)}px) `;
    // Brightness: 0 is black, 1 is original, > 1 is brighter
    if (brightness !== 1) filterString += `brightness(${Math.max(0, brightness)}) `;
    // Contrast: 0 is solid gray, 1 is original, > 1 is higher contrast
    if (contrast !== 1) filterString += `contrast(${Math.max(0, contrast)}) `;
    
    if (filterString.trim() !== '') {
        ctx.filter = filterString.trim();
    }
    
    ctx.drawImage(originalImg, 0, 0, canvas.width, canvas.height);
    ctx.filter = 'none'; // Reset filter: subsequent drawing operations are not affected

    // 2. Apply color tint overlay
    // tintOpacity: 0 (no tint) to 1 (full tint effect)
    // tintColorStr: "R,G,B" string
    const clampedTintOpacity = Math.max(0, Math.min(1, tintOpacity));
    if (clampedTintOpacity > 0 && tintColorStr && tintColorStr.trim() !== "") {
        const parsedColor_tint = tintColorStr.split(',').map(s => parseInt(s.trim(), 10));
        if (parsedColor_tint.length === 3 && parsedColor_tint.every(num => !isNaN(num) && num >= 0 && num <= 255)) {
            const [r, g, b] = parsedColor_tint;
            // 'overlay' blending mode often gives a nice, rich tint effect.
            // 'soft-light' is another option for a more subtle effect.
            ctx.globalCompositeOperation = 'overlay'; 
            ctx.fillStyle = `rgba(${r},${g},${b},${clampedTintOpacity})`;
            ctx.fillRect(0, 0, canvas.width, canvas.height);
            ctx.globalCompositeOperation = 'source-over'; // Reset to default blending mode
        } else {
            console.warn(`Invalid tintColorStr: "${tintColorStr}". Expected format "R,G,B" with values 0-255.`);
        }
    }

    // 3. Apply vignette effect
    // vignetteStrength: 0 (no vignette) to 1 (strongest vignette). Controls both spread and opacity.
    // vignetteColorStr: "R,G,B" string for the vignette color.
    const clampedVignetteStrength = Math.max(0, Math.min(1, vignetteStrength)); // Clamp strength [0,1]
    if (clampedVignetteStrength > 0 && vignetteColorStr && vignetteColorStr.trim() !== "") {
        const parsedColor_vignette = vignetteColorStr.split(',').map(s => parseInt(s.trim(), 10));
        if (parsedColor_vignette.length === 3 && parsedColor_vignette.every(num => !isNaN(num) && num >= 0 && num <= 255)) {
            const [vr, vg, vb] = parsedColor_vignette;
            const centerX = canvas.width / 2;
            const centerY = canvas.height / 2;

            // The radial gradient defines how the vignette color fades in from the center.
            // r0: Radius of the central area where the vignette is fully transparent.
            // r1: Radius where the vignette reaches its target color and opacity (usually image edges).
            const maxRadius = Math.sqrt(centerX * centerX + centerY * centerY);

            // vignetteStrength determines how far the clear area (r0) extends.
            // strength = 0: r0 = maxRadius (fully clear).
            // strength = 1: r0 = 0 (effect starts from center).
            const r0 = maxRadius * (1 - clampedVignetteStrength);
            const r1 = maxRadius;

            if (r0 < r1) { // Ensure r0 is less than r1 for a valid gradient.
                const gradient = ctx.createRadialGradient(centerX, centerY, r0, centerX, centerY, r1);
            
                // Start of the gradient (at r0): vignette color, but fully transparent.
                gradient.addColorStop(0, `rgba(${vr},${vg},${vb},0)`); 
                // End of the gradient (at r1): vignette color with opacity determined by vignetteStrength.
                gradient.addColorStop(1, `rgba(${vr},${vg},${vb},${clampedVignetteStrength})`); 

                // Apply the gradient fill. Default 'source-over' composite op will overlay this.
                ctx.fillStyle = gradient;
                ctx.fillRect(0, 0, canvas.width, canvas.height);
            }
            // If r0 >= r1 (e.g., strength is 0 or very near 0), no visible vignette is drawn, which is correct.
        } else {
            console.warn(`Invalid vignetteColorStr: "${vignetteColorStr}". Expected format "R,G,B" with values 0-255.`);
        }
    }
    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

Whimsical Photo Filter is an online image editing tool that allows users to enhance their photos using various effects. It enables you to adjust saturation, brightness, contrast, and blur to create a desired look. Additionally, users can apply color tints and vignette effects to add artistic flair to their images. This tool can be beneficial for photographers, social media enthusiasts, and anyone looking to enhance the aesthetic of their images for personal use, professional portfolios, or sharing on platforms like Instagram.

Leave a Reply

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