Please bookmark this page to avoid losing your image tool!

Image Treasure Map 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, intensity = 1.0, agingColorStr = "200,170,120", darkenEdgesFactor = 0.7, noiseAmount = 20, detailContrast = 1.5) {
    const canvas = document.createElement('canvas');
    // Using { willReadFrequently: true } can be an optimization hint for some browsers
    const ctx = canvas.getContext('2d', { willReadFrequently: true }); 
    
    canvas.width = originalImg.naturalWidth || originalImg.width;
    canvas.height = originalImg.naturalHeight || originalImg.height;

    // If image has no dimensions (e.g., not loaded yet or invalid), return empty canvas
    if (canvas.width === 0 || canvas.height === 0) {
        // console.warn("Image has zero dimensions. Returning empty canvas."); // Optional warning
        return 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 if the image is cross-origin and canvas is tainted
        // console.error("Could not getImageData due to cross-origin restrictions or other error: ", e);
        // In this case, return the canvas with the original image drawn, as processing is not possible.
        return canvas; 
    }
    
    const data = imageData.data;
    const width = canvas.width;
    const height = canvas.height;

    // Parse agingColorStr: "R,G,B" and set defaults if parsing fails or invalid
    const defaultAgingR = 200, defaultAgingG = 170, defaultAgingB = 120;
    let paperR = defaultAgingR, paperG = defaultAgingG, paperB = defaultAgingB;
    const colorParts = agingColorStr.split(',');

    if (colorParts.length === 3) {
        const rVal = parseInt(colorParts[0].trim(), 10);
        const gVal = parseInt(colorParts[1].trim(), 10);
        const bVal = parseInt(colorParts[2].trim(), 10);
        
        // Ensure all parsed values are finite numbers before using them
        if (Number.isFinite(rVal) && Number.isFinite(gVal) && Number.isFinite(bVal)) {
            paperR = Math.max(0, Math.min(255, rVal));
            paperG = Math.max(0, Math.min(255, gVal));
            paperB = Math.max(0, Math.min(255, bVal));
        }
    }

    // Define base ink color (dark, desaturated)
    const inkR_base = 40, inkG_base = 30, inkB_base = 20;

    // Clamp intensity and darkenEdgesFactor to a [0, 1] range for safety
    const safeIntensity = Math.max(0, Math.min(1, intensity));
    const safeDarkenEdgesFactor = Math.max(0, Math.min(1, darkenEdgesFactor));
    const safeNoiseAmount = Math.max(0, noiseAmount); // Noise cannot be negative

    for (let i = 0; i < data.length; i += 4) {
        const r_orig = data[i];
        const g_orig = data[i+1];
        const b_orig = data[i+2];
        // const a_orig = data[i+3]; // Original alpha is preserved

        // Initialize effect colors with original pixel values
        let r_effect = r_orig;
        let g_effect = g_orig;
        let b_effect = b_orig;

        // 1. Convert to grayscale (luminosity method) as basis for tinting
        const gray = 0.299 * r_effect + 0.587 * g_effect + 0.114 * b_effect;
        
        // 2. Apply aging color: Lerp between ink and paper color based on grayscale
        const normGray = gray / 255; // Normalized grayscale (0=black, 1=white)
        r_effect = inkR_base * (1 - normGray) + paperR * normGray;
        g_effect = inkG_base * (1 - normGray) + paperG * normGray;
        b_effect = inkB_base * (1 - normGray) + paperB * normGray;
        
        // Clamp after color transformation
        r_effect = Math.max(0, Math.min(255, r_effect));
        g_effect = Math.max(0, Math.min(255, g_effect));
        b_effect = Math.max(0, Math.min(255, b_effect));

        // 3. Increase Contrast for details/ink
        if (detailContrast !== 1.0) { // Avoid processing if no change intended
            const contrastMidpoint = 128; 
            r_effect = (r_effect - contrastMidpoint) * detailContrast + contrastMidpoint;
            g_effect = (g_effect - contrastMidpoint) * detailContrast + contrastMidpoint;
            b_effect = (b_effect - contrastMidpoint) * detailContrast + contrastMidpoint;
            
            r_effect = Math.max(0, Math.min(255, r_effect));
            g_effect = Math.max(0, Math.min(255, g_effect));
            b_effect = Math.max(0, Math.min(255, b_effect));
        }

        // 4. Add Noise
        if (safeNoiseAmount > 0) {
            // Symmetrical noise: adds or subtracts randomly
            const noiseVal = (Math.random() - 0.5) * safeNoiseAmount; 
            r_effect += noiseVal;
            g_effect += noiseVal;
            b_effect += noiseVal;
            
            r_effect = Math.max(0, Math.min(255, r_effect));
            g_effect = Math.max(0, Math.min(255, g_effect));
            b_effect = Math.max(0, Math.min(255, b_effect));
        }

        // 5. Vignette (Darken Edges)
        if (safeDarkenEdgesFactor > 0) {
            const x = (i / 4) % width;
            const y = Math.floor((i / 4) / width);
            
            const dx = x - width / 2;
            const dy = y - height / 2;
            
            const maxDist = Math.sqrt( (width/2)**2 + (height/2)**2 );
             // Avoid division by zero for tiny images if maxDist is 0
            if (maxDist > 0) {
                const currentDist = Math.sqrt(dx*dx + dy*dy);
                let vignetteAmount = currentDist / maxDist; 
                // Power curve for falloff: higher exponent means vignette is more concentrated at edges
                vignetteAmount = Math.pow(vignetteAmount, 2.5); 
                
                const darkening = 1.0 - (vignetteAmount * safeDarkenEdgesFactor);
                
                r_effect *= darkening;
                g_effect *= darkening;
                b_effect *= darkening;

                r_effect = Math.max(0, Math.min(255, r_effect));
                g_effect = Math.max(0, Math.min(255, g_effect));
                b_effect = Math.max(0, Math.min(255, b_effect));
            }
        }

        // Final blend: Mix original pixel with the fully effected pixel based on `safeIntensity`
        data[i]   = r_orig * (1 - safeIntensity) + r_effect * safeIntensity;
        data[i+1] = g_orig * (1 - safeIntensity) + g_effect * safeIntensity;
        data[i+2] = b_orig * (1 - safeIntensity) + b_effect * safeIntensity;
        // Alpha (data[i+3]) remains untouched (effectively data[i+3] = a_orig; from original imageData)
        
        // Final clamp (mostly for safety, as individual steps should ideally have clamped values correctly)
        data[i]   = Math.max(0, Math.min(255, data[i]));
        data[i+1] = Math.max(0, Math.min(255, data[i+1]));
        data[i+2] = Math.max(0, Math.min(255, data[i+2]));
    }

    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 Treasure Map Filter Effect Tool is a web-based utility that allows users to apply a vintage, treasure-map-like effect to their images. By adjusting parameters such as intensity, aging color, darkening edges, noise amount, and detail contrast, users can customize their images to have a nostalgic and artistic appearance. This tool is particularly useful for creating visually appealing social media posts, enhancing artwork, or generating unique graphics for invitations and themed events. Whether you are a photographer, designer, or hobbyist, this tool can help you transform your images into creative works reminiscent of old maps.

Leave a Reply

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