Please bookmark this page to avoid losing your image tool!

Terra Incognita Image Generator

(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, sepiaIntensity = "0.85", drawGridLines = "1") {
    const intensity = Math.min(1, Math.max(0, parseFloat(sepiaIntensity)));
    const addGrid = parseInt(drawGridLines) === 1;

    const width = originalImg.width;
    const height = originalImg.height;

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

    // 1. Draw original image
    ctx.drawImage(originalImg, 0, 0);

    // 2. Apply Sepia & Antique tone to the image
    const imgData = ctx.getImageData(0, 0, width, height);
    const data = imgData.data;

    for (let i = 0; i < data.length; i += 4) {
        let r = data[i], g = data[i + 1], b = data[i + 2];
        
        // Standard Sepia formula
        let tr = r * 0.393 + g * 0.769 + b * 0.189;
        let tg = r * 0.349 + g * 0.686 + b * 0.168;
        let tb = r * 0.272 + g * 0.534 + b * 0.131;

        // Enhance for parchment warmth
        tr *= 1.15;
        tg *= 1.05;
        tb *= 0.90;

        // Blend with original
        data[i] = Math.min(255, r + (tr - r) * intensity);
        data[i + 1] = Math.min(255, g + (tg - g) * intensity);
        data[i + 2] = Math.min(255, b + (tb - b) * intensity);
    }
    ctx.putImageData(imgData, 0, 0);

    // 3. Create "Terra Incognita" Fog surrounding the center
    ctx.save();
    // Cap blur to prevent massive lag on 4K+ images
    const blurAmount = Math.min(60, Math.max(width, height) * 0.04);
    ctx.filter = `blur(${blurAmount}px)`;
    
    // Outer frame path mapping the unexplored area
    ctx.fillStyle = '#cdb492'; // Classic map parchment tone
    ctx.beginPath();
    ctx.moveTo(0, 0);
    ctx.lineTo(width, 0);
    ctx.lineTo(width, height);
    ctx.lineTo(0, height);
    ctx.lineTo(0, 0);
    
    // Cut out an organic hole in the middle mapping explored lands
    const cx = width / 2;
    const cy = height / 2;
    const baseRadius = Math.min(width, height) * 0.38;
    const segments = 150;
    
    for (let i = 0; i <= segments; i++) {
        const angle = -i * (Math.PI * 2) / segments; // reverse map for inner cutout
        
        // Complex sine combinations for organic, hand-drawn edges
        const rOffset = Math.sin(i * 12) * (baseRadius * 0.06) + 
                        Math.sin(i * 5) * (baseRadius * 0.09) + 
                        Math.sin(i * 27) * (baseRadius * 0.03) + 
                        Math.random() * (baseRadius * 0.03);
                        
        const r = baseRadius + rOffset;
        const px = cx + Math.cos(angle) * r;
        const py = cy + Math.sin(angle) * r;
        
        if (i === 0) ctx.moveTo(px, py);
        else ctx.lineTo(px, py);
    }
    ctx.fill();
    ctx.restore();

    // 4. Map Grids (Latitude / Longitude emulation)
    if (addGrid) {
        ctx.strokeStyle = `rgba(101, 67, 33, 0.25)`; 
        ctx.lineWidth = Math.max(1, width * 0.002);
        let gridSize = Math.max(width, height) / 12;
        
        ctx.beginPath();
        for (let x = cx % gridSize; x < width; x += gridSize) {
            ctx.moveTo(x, 0);
            ctx.lineTo(x, height);
        }
        for (let y = cy % gridSize; y < height; y += gridSize) {
            ctx.moveTo(0, y);
            ctx.lineTo(width, y);
        }
        ctx.stroke();
    }

    // 5. Paper texture/grain via lower-resolution canvas upscaling
    const nWidth = Math.max(1, Math.floor(width / 3));
    const nHeight = Math.max(1, Math.floor(height / 3));
    const noiseCanvas = document.createElement('canvas');
    noiseCanvas.width = nWidth;
    noiseCanvas.height = nHeight;
    const nCtx = noiseCanvas.getContext('2d');
    const nData = nCtx.createImageData(nWidth, nHeight);
    
    for(let i = 0; i < nData.data.length; i += 4) {
        let val = Math.random() * 255;
        nData.data[i] = val;         // R
        nData.data[i+1] = val * 0.9; // G 
        nData.data[i+2] = val * 0.8; // B
        nData.data[i+3] = 40;        // Soft opacity
    }
    nCtx.putImageData(nData, 0, 0);

    ctx.globalCompositeOperation = 'multiply';
    ctx.drawImage(noiseCanvas, 0, 0, nWidth, nHeight, 0, 0, width, height);
    ctx.globalCompositeOperation = 'source-over';

    // 6. Draw Vignette Gradient Overlay
    const radGrad = ctx.createRadialGradient(cx, cy, baseRadius * 0.5, cx, cy, Math.max(width, height) * 0.8);
    radGrad.addColorStop(0, 'rgba(0, 0, 0, 0)');
    radGrad.addColorStop(1, 'rgba(70, 40, 10, 0.55)');
    ctx.fillStyle = radGrad;
    ctx.fillRect(0, 0, width, height);

    // 7. Draw Stylized Compass Rose (Procedural)
    const compassRadius = Math.min(width, height) * 0.08;
    const padding = compassRadius * 1.5;
    
    if (width > padding * 2.5 && height > padding * 2.5) {
        drawCompass(ctx, padding, height - padding, compassRadius);
    }

    // 8. Add "Terra Incognita" text marker
    ctx.fillStyle = 'rgba(60, 25, 5, 0.85)';
    const fontSize = Math.max(20, Math.min(width, height) * 0.045);
    ctx.font = `italic 700 ${fontSize}px "Times New Roman", Times, serif`;
    ctx.textAlign = 'right';
    ctx.textBaseline = 'bottom';
    
    // Add text shadow to help it stand out
    ctx.shadowColor = "rgba(220, 200, 170, 0.8)";
    ctx.shadowOffsetX = 1;
    ctx.shadowOffsetY = 1;
    ctx.shadowBlur = 0;
    ctx.fillText("Terra Incognita", width - (padding * 0.5), height - (padding * 0.5));
    ctx.shadowColor = "transparent";

    return canvas;

    // Helper block explicitly for procedural compass rose rendering
    function drawCompass(c, x, y, r) {
        c.save();
        c.translate(x, y);

        // Compass borders
        c.strokeStyle = 'rgba(70, 30, 10, 0.7)';
        c.lineWidth = Math.max(1, r * 0.03);
        c.beginPath(); c.arc(0, 0, r, 0, Math.PI * 2); c.stroke();
        c.beginPath(); c.arc(0, 0, r * 0.85, 0, Math.PI * 2); c.stroke();

        const colorLight = 'rgba(210, 180, 140, 0.95)';
        const colorDark = 'rgba(80, 35, 10, 0.95)';

        const drawDiamondSide = (angle, len, wide, color) => {
            c.save();
            c.rotate(angle);
            c.fillStyle = color;
            c.beginPath();
            c.moveTo(0, -len);
            c.lineTo(wide, 0);
            c.lineTo(0, 0);
            c.fill();
            c.restore();
        };

        // Draw compass star points
        for(let i = 0; i < 8; i++) {
            let angle = i * Math.PI / 4;
            let length = (i % 2 === 0) ? r * 0.95 : r * 0.6; // Alternating lengths
            let widthSpan = r * 0.15;
            
            // Light side
            drawDiamondSide(angle, length, widthSpan, colorLight);
            // Dark side
            drawDiamondSide(angle, length, -widthSpan, colorDark);
        }

        // Inner compass pip
        c.beginPath();
        c.arc(0, 0, r * 0.1, 0, Math.PI * 2);
        c.fillStyle = colorDark;
        c.fill();

        c.restore();
    }
}

Free Image Tool Creator

Can't find the image tool you're looking for?
Create one based on your own needs now!

Description

The Terra Incognita Image Generator transforms standard photos into stylized, antique-looking maps. The tool applies a warm sepia tone and parchment texture to your images, adds a decorative fog effect around the edges to create an ‘unexplored’ aesthetic, and overlays nautical elements such as grid lines, a procedural compass rose, and vintage text. This is an ideal tool for tabletop gamers, fantasy writers, or anyone looking to create thematic assets for world-building, role-playing games, or artistic projects with a historical explorer vibe.

Leave a Reply

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