Please bookmark this page to avoid losing your image tool!

Image Studio Ghibli 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.
async function processImage(originalImg, intensity = 0.5, saturationBoost = 0.2, contrastBoost = 0.1, ambienceHueShift = 5) {
    // Helper function to ensure glfx.js is loaded
    async function ensureGlfxLoaded() {
        if (typeof window.fx !== 'undefined') {
            return true; // Already loaded
        }
        if (window._glfxLoadingPromise) {
            try {
                await window._glfxLoadingPromise;
                return typeof window.fx !== 'undefined';
            } catch (e) {
                return false;
            }
        }

        window._glfxLoadingPromise = new Promise((resolve, reject) => {
            const script = document.createElement('script');
            script.src = 'https://evanw.github.io/glfx.js/glfx.js';
            script.id = 'glfx-script-dynamic-loader';
            
            script.onload = () => {
                delete window._glfxLoadingPromise;
                if (typeof window.fx !== 'undefined') {
                    console.log("glfx.js loaded successfully.");
                    resolve();
                } else {
                    console.error("glfx.js loaded but window.fx is undefined.");
                    reject(new Error("glfx.js loaded but window.fx is undefined."));
                }
            };
            script.onerror = (err) => {
                delete window._glfxLoadingPromise;
                const existingScript = document.getElementById('glfx-script-dynamic-loader');
                if (existingScript) {
                    existingScript.remove();
                }
                console.error("Failed to load glfx.js script:", err);
                reject(new Error("Failed to load glfx.js script."));
            };
            document.head.appendChild(script);
        });

        try {
            await window._glfxLoadingPromise;
            return typeof window.fx !== 'undefined';
        } catch (e) {
            return false;
        }
    }

    const canvasWidth = originalImg.naturalWidth || originalImg.width;
    const canvasHeight = originalImg.naturalHeight || originalImg.height;

    if (canvasWidth === 0 || canvasHeight === 0) {
        console.error("Image has zero dimensions.");
        const emptyCanvas = document.createElement('canvas');
        emptyCanvas.width = 1;
        emptyCanvas.height = 1;
        return emptyCanvas;
    }
    
    // Coerce parameters to numbers and provide defaults if they are not valid numbers
    const numIntensity = Number(intensity);
    const _intensity = !isNaN(numIntensity) ? Math.max(0, Math.min(1, numIntensity)) : 0.5;

    const numSaturationBoost = Number(saturationBoost);
    const _saturationBoostInput = !isNaN(numSaturationBoost) ? Math.max(0, Math.min(1, numSaturationBoost)) : 0.2;
    
    const numContrastBoost = Number(contrastBoost);
    const _contrastBoostInput = !isNaN(numContrastBoost) ? Math.max(0, Math.min(1, numContrastBoost)) : 0.1;

    const numAmbienceHueShift = Number(ambienceHueShift);
    const _ambienceHueShiftEffect = !isNaN(numAmbienceHueShift) ? numAmbienceHueShift : 5;


    let useGlfx = await ensureGlfxLoaded();

    if (useGlfx) {
        try {
            const fxCanvas = window.fx.canvas();
            const texture = fxCanvas.texture(originalImg);

            // Parameters for glfx.js filters
            // Map input (0-1) to suitable ranges for glfx.js filters
            const _saturationBoostEffect = _saturationBoostInput * 0.7; // Max 0.7 for glfx saturation (-1 to 1)
            const _contrastBoostEffect = _contrastBoostInput * 0.5;   // Max 0.5 for glfx contrast (-1 to 1)

            const denoiseStrength = 5 + _intensity * 25;      // Range: 5 (low intensity) to 30 (high intensity)
            const unsharpRadius = 10 + _intensity * 30;       // Range: 10 to 40 pixels
            const unsharpStrength = 0.1 + _intensity * 0.4;   // Range: 0.1 (subtle) to 0.5 (strong)

            fxCanvas.draw(texture)
                .denoise(denoiseStrength)
                .brightnessContrast(0, _contrastBoostEffect) // Brightness neutral, only boosting contrast
                .hueSaturation(_ambienceHueShiftEffect, _saturationBoostEffect)
                .unsharpMask(unsharpRadius, unsharpStrength)
                .update();
            return fxCanvas; // This is an HTMLCanvasElement
        } catch (glfxError) {
            console.error("Error during glfx.js processing, falling back to 2D canvas.", glfxError);
            useGlfx = false; // Force fallback
        }
    }
    
    // Fallback logic if glfx.js is not available or failed
    console.warn("Using fallback 2D canvas rendering for Ghibli filter.");
    const fallbackCanvas = document.createElement('canvas');
    fallbackCanvas.width = canvasWidth;
    fallbackCanvas.height = canvasHeight;
    const ctx = fallbackCanvas.getContext('2d');

    // CSS filter string for 2D context - uses percentage for saturate/contrast
    const cssSaturation = 100 + _saturationBoostInput * 100; // e.g., 0.2 input -> 120%
    const cssContrast = 100 + _contrastBoostInput * 100;   // e.g., 0.1 input -> 110%
    const cssFilterString = `hue-rotate(${_ambienceHueShiftEffect}deg) saturate(${cssSaturation}%) contrast(${cssContrast}%)`;
    
    if (typeof ctx.filter !== 'undefined') {
        let canUseFilter = false;
        try {
            // Test if setting a basic filter works; some environments might have `ctx.filter` but it may not be fully functional.
            ctx.filter = 'blur(0px)'; 
            ctx.filter = 'none'; // Reset immediately.
            canUseFilter = true;
        } catch (e) {
            // If fails, canUseFilter remains false.
            console.warn("ctx.filter property exists but is not operational.", e);
        }

        if (canUseFilter) {
            try {
                ctx.filter = cssFilterString;
                ctx.drawImage(originalImg, 0, 0, fallbackCanvas.width, fallbackCanvas.height);
                ctx.filter = 'none'; // Reset filter style; content remains filtered.
            } catch (e_filter_apply) {
                console.warn("Applying 2D canvas filter string failed. Drawing original image.", e_filter_apply);
                ctx.filter = 'none'; // Ensure it's reset
                ctx.drawImage(originalImg, 0, 0, fallbackCanvas.width, fallbackCanvas.height); // Draw original
            }
        } else {
            ctx.drawImage(originalImg, 0, 0, fallbackCanvas.width, fallbackCanvas.height); // Draw original
        }
    } else {
        console.warn("ctx.filter is not supported by this browser. Drawing original image.");
        ctx.drawImage(originalImg, 0, 0, fallbackCanvas.width, fallbackCanvas.height); // Draw original
    }
    return fallbackCanvas;
}

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 Studio Ghibli Photo Filter tool allows users to apply a unique visual filter inspired by the art style of Studio Ghibli to their images. This tool provides controls for adjusting intensity, saturation, contrast, and hue, enabling users to enhance their photos with a whimsical, cinematic quality. It can be used for artistic projects, enhancing personal photos, or creating themed content for social media that evokes the charm of Studio Ghibli films.

Leave a Reply

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