Please bookmark this page to avoid losing your image tool!

Photo Sanskrit Text 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.
async function processImage(
    originalImg, 
    textColor = "rgba(0, 0, 0, 0.5)", 
    fontSize = 24, 
    spacing = 30, 
    sanskritChars = "ॐअआइईउऊऋॠऌएऐओऔकखगघङचछजझञटठडढणतथदधनपफबभमयलरवशषसह", 
    fontFamily = "Noto Sans Devanagari", 
    randomDisplacementFactor = 0.5,
    textBlendMode = "source-over"
) {

    // Helper function to ensure a specific font is loaded via CDN (Google Fonts in this case)
    const _ensureFontLoaded = async (fontFamilyToLoad, cssUrl) => {
        if (typeof document === 'undefined' || !document.fonts) {
            console.warn("document.fonts API not available. Font loading relies on system availability.");
            return; // Running in an environment without document or document.fonts (e.g., Node.js without JSDOM)
        }

        // Check if font is already loaded/available
        try {
            if (document.fonts.check(`1em "${fontFamilyToLoad}"`)) {
                // console.log(`Font "${fontFamilyToLoad}" is already available.`);
                return;
            }
        } catch (e) {
            console.warn(`Error checking font "${fontFamilyToLoad}": ${e}. Assuming not loaded.`);
        }
        

        // Check if the stylesheet link tag already exists
        // Normalize URL for comparison
        const absoluteCssUrl = new URL(cssUrl, document.baseURI).href;
        let existingLink = null;
        const links = document.head.querySelectorAll('link[rel="stylesheet"]');
        for (let i = 0; i < links.length; i++) {
            if (links[i].href === absoluteCssUrl) {
                existingLink = links[i];
                break;
            }
        }
        
        if (existingLink) {
            // console.log(`Stylesheet ${absoluteCssUrl} already linked. Attempting to load font "${fontFamilyToLoad}".`);
            try {
                await document.fonts.load(`1em "${fontFamilyToLoad}"`);
                // console.log(`Font "${fontFamilyToLoad}" loaded from existing stylesheet.`);
            } catch (e) {
                console.warn(`Could not load font "${fontFamilyToLoad}" from existing stylesheet ${absoluteCssUrl}: ${e}. Will attempt to use system fallback.`);
            }
            return;
        }
        
        // If stylesheet not found, create and append it
        return new Promise((resolve) => {
            const link = document.createElement('link');
            link.rel = 'stylesheet';
            link.href = cssUrl; // Use original cssUrl for href attribute
            link.onload = async () => {
                try {
                    await document.fonts.load(`1em "${fontFamilyToLoad}"`);
                    // console.log(`Font "${fontFamilyToLoad}" loaded successfully via new stylesheet ${absoluteCssUrl}.`);
                    resolve();
                } catch (e) {
                    console.warn(`Error loading font "${fontFamilyToLoad}" after stylesheet ${absoluteCssUrl} injection: ${e}. Will attempt to use system fallback.`);
                    resolve(); 
                }
            };
            link.onerror = () => {
                console.error(`Failed to load stylesheet for font "${fontFamilyToLoad}" from ${absoluteCssUrl}. Will attempt to use system fallback.`);
                resolve();
            };
            document.head.appendChild(link);
        });
    };

    // Parameter sanitization
    fontSize = Math.max(1, Number(fontSize) || 24);
    spacing = Math.max(5, Number(spacing) || 30); // Min 5px spacing to avoid excessive density
    randomDisplacementFactor = Math.max(0, Math.min(1, Number(randomDisplacementFactor) || 0.5));
    sanskritChars = String(sanskritChars || "ॐ"); // Ensure it's a string, default to Om if empty/null

    // Attempt to load the specified Devanagari font if it's the default one
    if (fontFamily.toLowerCase() === "noto sans devanagari") {
        const googleFontUrl = 'https://fonts.googleapis.com/css2?family=Noto+Sans+Devanagari&display=swap';
        try {
            await _ensureFontLoaded('Noto Sans Devanagari', googleFontUrl);
        } catch (e) {
            // This catch should ideally not be hit due to _ensureFontLoaded always resolving.
            // Kept for safety.
            console.warn("An issue occurred during font loading. Proceeding with system fonts.");
        }
    }
    
    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d', { willReadFrequently: false });

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

    if (imgWidth === 0 || imgHeight === 0) {
        console.error("Image has zero dimensions. Cannot process.");
        // Return an empty (or very small) canvas to avoid errors downstream
        canvas.width = 1;
        canvas.height = 1;
        return canvas;
    }

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

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

    // Prepare for text drawing
    const chars = sanskritChars.split('');
    if (chars.length === 0) {
        console.warn("Sanskrit characters string is empty. No text will be drawn.");
        return canvas; // Return canvas with only original image
    }

    ctx.fillStyle = textColor;
    // Enclose font family name in quotes if it contains spaces or is not a generic family.
    // The generic fallback 'sans-serif' helps if the custom font fails.
    const safeFontFamily = fontFamily.includes(' ') || !['serif', 'sans-serif', 'monospace', 'cursive', 'fantasy'].includes(fontFamily.toLowerCase()) 
                           ? `"${fontFamily}"` 
                           : fontFamily;
    ctx.font = `${fontSize}px ${safeFontFamily}, sans-serif`;
    ctx.textAlign = "center";
    ctx.textBaseline = "middle";
    
    // Apply blend mode for text
    if (typeof ctx.globalCompositeOperation === 'string' && textBlendMode) {
         const validBlendModes = ["source-over", "source-in", "source-out", "source-atop", 
                                 "destination-over", "destination-in", "destination-out", "destination-atop",
                                 "lighter", "copy", "xor", "multiply", "screen", "overlay", "darken", 
                                 "lighten", "color-dodge", "color-burn", "hard-light", "soft-light", 
                                 "difference", "exclusion", "hue", "saturation", "color", "luminosity"];
        if (validBlendModes.includes(textBlendMode)) {
            ctx.globalCompositeOperation = textBlendMode;
        } else {
            console.warn(`Invalid textBlendMode: "${textBlendMode}". Defaulting to "source-over".`);
            ctx.globalCompositeOperation = "source-over";
        }
    }


    // Distribute characters over the image
    // Loop through "cells" defined by spacing
    for (let y = 0; y < canvas.height; y += spacing) {
        for (let x = 0; x < canvas.width; x += spacing) {
            const charIndex = Math.floor(Math.random() * chars.length);
            const charToDraw = chars[charIndex];
            
            // Calculate actual drawing position: center of cell + random displacement
            const cellCenterX = x + spacing / 2;
            const cellCenterY = y + spacing / 2;

            const offsetX = (Math.random() - 0.5) * spacing * randomDisplacementFactor;
            const offsetY = (Math.random() - 0.5) * spacing * randomDisplacementFactor;

            const drawX = cellCenterX + offsetX;
            const drawY = cellCenterY + offsetY;
            
            ctx.fillText(charToDraw, drawX, drawY);
        }
    }
    
    // Reset globalCompositeOperation if it was changed, for good measure
    // although nothing else is drawn on this canvas in this function after text.
    if (ctx.globalCompositeOperation !== "source-over" && textBlendMode !== "source-over") {
        ctx.globalCompositeOperation = "source-over"; 
    }

    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 Photo Sanskrit Text Filter Effect Tool allows users to overlay stylized Sanskrit text on images. Users can customize various parameters such as text color, font size, spacing, and font family to create unique designs. With its ability to blend the text with the image, this tool is ideal for enhancing photos for cultural or artistic projects, creating educational materials, or producing decorative images that incorporate traditional Sanskrit characters. Whether for personal use or professional design, this tool provides a creative way to visualize text in an artistic context.

Leave a Reply

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