Please bookmark this page to avoid losing your image tool!

Image Hourglass Sand 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.
function processImage(originalImg, sandColorStr = "210,180,140", noiseIntensity = 30, monochromeStrength = 0.7) {
    // Parameter validation and parsing for sandColorStr
    let sandTargetR = 210, sandTargetG = 180, sandTargetB = 140; // Default sand color (Tan like)
    
    if (typeof sandColorStr === 'string') {
        const parts = sandColorStr.split(',').map(s => parseInt(s.trim(), 10));
        if (parts.length === 3 && parts.every(p => !isNaN(p) && p >= 0 && p <= 255)) {
            [sandTargetR, sandTargetG, sandTargetB] = parts;
        } else {
            console.warn(`Invalid sandColorStr format: "${sandColorStr}". Using default sand color (${sandTargetR},${sandTargetG},${sandTargetB}).`);
        }
    } else {
        // Covers cases where sandColorStr might be null, undefined (if default wasn't applied), or wrong type
        console.warn(`sandColorStr is not a string or is invalid: ${sandColorStr}. Using default sand color.`);
    }

    // Validate and clamp noiseIntensity and monochromeStrength
    // Ensure they are numbers, provide a default of 0 if conversion fails (e.g. NaN), then clamp.
    const effNoiseIntensity = Math.max(0, Number(noiseIntensity) || 0);
    const effMonochromeStrength = Math.max(0, Math.min(1, Number(monochromeStrength) || 0));

    const canvas = document.createElement('canvas');
    // Add willReadFrequently hint for potential performance improvement with getImageData
    const ctx = canvas.getContext('2d', { willReadFrequently: true });

    // Determine image dimensions
    const imgWidth = originalImg.naturalWidth || originalImg.width;
    const imgHeight = originalImg.naturalHeight || originalImg.height;

    if (imgWidth === 0 || imgHeight === 0) {
        // Handle cases like unloaded or broken image
        console.warn("Image has zero dimensions. Returning an empty canvas.");
        canvas.width = 0;
        canvas.height = 0;
        return canvas;
    }

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

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

    let imageData;
    try {
        imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
    } catch (e) {
        // This can happen due to tainted canvas (e.g., cross-origin image without CORS)
        console.error("Could not get image data likely due to CORS or other security restrictions: ", e);
        // Return the canvas with the original image drawn (if successful), but without the filter.
        // The user of this function should ensure the image is usable (same-origin or CORS-enabled).
        return canvas;
    }
    
    const data = imageData.data;

    // Iterate over each pixel
    for (let i = 0; i < data.length; i += 4) {
        const r = data[i];
        const g = data[i + 1];
        const b = data[i + 2];
        // Alpha channel (data[i+3]) is preserved

        // 1. Calculate luminance (perceived brightness/grayscale value)
        const lum = 0.299 * r + 0.587 * g + 0.114 * b;

        // 2. Create the monochrome tinted color:
        // This maps the luminance to the target sand color.
        // Black (lum=0) remains black. White (lum=255) becomes the full sandTarget color.
        const monoTintR = (lum / 255) * sandTargetR;
        const monoTintG = (lum / 255) * sandTargetG;
        const monoTintB = (lum / 255) * sandTargetB;
        
        // 3. Blend original color with the monochrome tinted color
        // monochromeStrength = 0 means original color, 1 means full monochrome tint.
        const blendR = (1 - effMonochromeStrength) * r + effMonochromeStrength * monoTintR;
        const blendG = (1 - effMonochromeStrength) * g + effMonochromeStrength * monoTintG;
        const blendB = (1 - effMonochromeStrength) * b + effMonochromeStrength * monoTintB;

        // 4. Add noise to each color channel independently for a grainy "sand" effect
        // Noise ranges from -effNoiseIntensity to +effNoiseIntensity
        const noiseValR = (Math.random() * 2 - 1) * effNoiseIntensity;
        const noiseValG = (Math.random() * 2 - 1) * effNoiseIntensity;
        const noiseValB = (Math.random() * 2 - 1) * effNoiseIntensity;
        
        const finalR = blendR + noiseValR;
        const finalG = blendG + noiseValG;
        const finalB = blendB + noiseValB;

        // 5. Clamp final RGB values to [0, 255] and round to the nearest integer
        data[i]   = Math.max(0, Math.min(255, Math.round(finalR)));
        data[i+1] = Math.max(0, Math.min(255, Math.round(finalG)));
        data[i+2] = Math.max(0, Math.min(255, Math.round(finalB)));
        // data[i+3] (alpha) remains unchanged
    }

    // Put the modified pixel data back onto the canvas
    ctx.putImageData(imageData, 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

The Image Hourglass Sand Filter Effect Tool allows users to apply a unique sand filter effect to their images. This tool enhances images by blending them with a monochrome tint that resembles sand, while also adding a grainy noise effect for a textured appearance. Users can customize the color of the sand and adjust the intensity of the grainy effect to suit their preferences. This tool is ideal for graphic designers, photographers, or anyone looking to add a distinctive, artistic filter to their images for use in social media, art projects, or personal collections.

Leave a Reply

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