Please bookmark this page to avoid losing your image tool!

Image Tilt-Shift Filter Application

(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, focusPosition = 0.5, focusRange = 0.2, blurAmount = 5, gradientType = "linear", angle = 0, saturation = 1.2, contrast = 1.1, transitionWidth = 0.15) {
    const width = originalImg.naturalWidth || originalImg.width;
    const height = originalImg.naturalHeight || originalImg.height;

    // Ensure parameters are numbers
    focusPosition = Number(focusPosition);
    focusRange = Number(focusRange);
    blurAmount = Number(blurAmount);
    angle = Number(angle);
    saturation = Number(saturation);
    contrast = Number(contrast);
    transitionWidth = Number(transitionWidth);

    // 1. Create sharpImageCanvas (with saturation/contrast)
    // This canvas will hold the original image modified by saturation and contrast.
    const sharpCanvas = document.createElement('canvas');
    sharpCanvas.width = width;
    sharpCanvas.height = height;
    const sharpCtx = sharpCanvas.getContext('2d');
    
    let sharpFilterString = '';
    if (saturation !== 1) sharpFilterString += `saturate(${saturation}) `;
    if (contrast !== 1) sharpFilterString += `contrast(${contrast})`;
    if (sharpFilterString) sharpCtx.filter = sharpFilterString.trim();
    
    sharpCtx.drawImage(originalImg, 0, 0, width, height);
    sharpCtx.filter = 'none'; // Reset filter

    // 2. Create blurredImageCanvas
    // This canvas will hold the version of the image that is blurred, saturated, and contrasted.
    const blurCanvas = document.createElement('canvas');
    blurCanvas.width = width;
    blurCanvas.height = height;
    const blurCtx = blurCanvas.getContext('2d');

    if (blurAmount > 0) {
        let blurFilterString = `blur(${blurAmount}px) `;
        if (saturation !== 1) blurFilterString += `saturate(${saturation}) `;
        if (contrast !== 1) blurFilterString += `contrast(${contrast})`;
        
        blurCtx.filter = blurFilterString.trim();
        blurCtx.drawImage(originalImg, 0, 0, width, height); // Draw original, filters apply
        blurCtx.filter = 'none'; // Reset filter
    } else {
        // If no blur, the "blurred" version is just the sharp (saturated/contrasted) version.
        blurCtx.drawImage(sharpCanvas, 0, 0, width, height);
    }

    // 3. Create alphaMaskCanvas
    // This canvas defines which parts of the image are sharp (white regions of mask) and blurred (black regions of mask).
    const alphaMaskCanvas = document.createElement('canvas');
    alphaMaskCanvas.width = width;
    alphaMaskCanvas.height = height;
    const maskCtx = alphaMaskCanvas.getContext('2d');

    // Clamp relative parameters to [0,1] range
    focusPosition = Math.max(0, Math.min(1, focusPosition));
    focusRange = Math.max(0, Math.min(1, focusRange));
    transitionWidth = Math.max(0, Math.min(1, transitionWidth));


    if (gradientType === "linear") {
        const tempMaskLinearCanvas = document.createElement('canvas');
        tempMaskLinearCanvas.width = width;
        tempMaskLinearCanvas.height = height;
        const tempMaskLinearCtx = tempMaskLinearCanvas.getContext('2d');

        tempMaskLinearCtx.translate(width / 2, height / 2);
        tempMaskLinearCtx.rotate(angle * Math.PI / 180);
        tempMaskLinearCtx.translate(-width / 2, -height / 2);

        const grad = tempMaskLinearCtx.createLinearGradient(0, 0, 0, height); // Gradient along Y-axis of rotated context

        const pSharpStart = focusPosition - focusRange / 2;
        const pSharpEnd = focusPosition + focusRange / 2;
        const pTransStartLead = pSharpStart - transitionWidth;
        const pTransEndTrail = pSharpEnd + transitionWidth;

        grad.addColorStop(0, 'black'); // Ensure start is blurred
        grad.addColorStop(Math.max(0, Math.min(1, pTransStartLead)), 'black');
        grad.addColorStop(Math.max(0, Math.min(1, pSharpStart)), 'white');
        grad.addColorStop(Math.max(0, Math.min(1, pSharpEnd)), 'white');
        grad.addColorStop(Math.max(0, Math.min(1, pTransEndTrail)), 'black');
        grad.addColorStop(1, 'black'); // Ensure end is blurred
        
        tempMaskLinearCtx.fillStyle = grad;
        tempMaskLinearCtx.fillRect(0, 0, width, height);
        
        maskCtx.drawImage(tempMaskLinearCanvas, 0, 0);

    } else if (gradientType === "radial") {
        const centerX = width / 2;
        const centerY = height / 2;
        
        const baseDim = Math.min(width, height);
        const sharpRadiusPx = baseDim * focusRange / 2;
        const transitionSizePx = baseDim * transitionWidth;
        const blurEndRadiusPx = sharpRadiusPx + transitionSizePx;

        if (focusRange === 0 && transitionWidth === 0) { // No focus effect defined, all sharp
             maskCtx.fillStyle = 'white';
             maskCtx.fillRect(0, 0, width, height);
        } else if (blurEndRadiusPx <= 0 && sharpRadiusPx <= 0) { // Effectively no focus or transition specified towards positive radius
             maskCtx.fillStyle = 'black'; // Make all blurred if radii are non-positive and not the specific "all_sharp" case
             maskCtx.fillRect(0, 0, width, height);
        }
        else {
            const grad = maskCtx.createRadialGradient(centerX, centerY, 0, centerX, centerY, Math.max(0.001, blurEndRadiusPx)); // blurEndRadiusPx must be > 0
            
            let stopSharpNormalized = 0;
            if (blurEndRadiusPx > 0) { // Avoid division by zero if blurEndRadiusPx is 0
               stopSharpNormalized = Math.max(0, Math.min(1, sharpRadiusPx / blurEndRadiusPx));
            } else if (sharpRadiusPx > 0) { // all sharp circle, no transition
                stopSharpNormalized = 1; // effectively make it all white up to sharpRadiusPx
            }


            grad.addColorStop(0, 'white');                     
            grad.addColorStop(stopSharpNormalized, 'white');   
            grad.addColorStop(1, 'black');                      
            
            maskCtx.fillStyle = grad;
            maskCtx.fillRect(0, 0, width, height); 
        }
    } else {
        // Default: unknown gradientType, make mask all white (fully sharp)
        maskCtx.fillStyle = 'white';
        maskCtx.fillRect(0, 0, width, height);
    }

    // 4. Create final outputCanvas and combine layers
    const outputCanvas = document.createElement('canvas');
    outputCanvas.width = width;
    outputCanvas.height = height;
    const outputCtx = outputCanvas.getContext('2d');

    outputCtx.drawImage(blurCanvas, 0, 0, width, height); // Draw blurred (or just sat/con) as base

    const tempSharpMaskedCanvas = document.createElement('canvas');
    tempSharpMaskedCanvas.width = width;
    tempSharpMaskedCanvas.height = height;
    const tempSharpMaskedCtx = tempSharpMaskedCanvas.getContext('2d');

    tempSharpMaskedCtx.drawImage(sharpCanvas, 0, 0, width, height); // sharp (sat/con) image
    tempSharpMaskedCtx.globalCompositeOperation = 'destination-in'; // Apply mask
    tempSharpMaskedCtx.drawImage(alphaMaskCanvas, 0, 0, width, height); // Mask (white=keep, black=discard)

    outputCtx.drawImage(tempSharpMaskedCanvas, 0, 0, width, height); // Draw masked sharp parts over base

    return outputCanvas;
}

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 Tilt-Shift Filter Application allows users to create a tilt-shift effect on their images, simulating a shallow depth of field. This effect can be adjusted with parameters such as focus position, focus range, and blur amount to enhance specific areas while blurring the rest of the image. Additional options for saturation and contrast adjustments are also available, providing further customization. This tool is ideal for photographers and graphic designers looking to emphasize subjects in their images or to create artistic effects that mimic miniatures or selective focus photography.

Leave a Reply

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