Please bookmark this page to avoid losing your image tool!

Image Traveler Creator

(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, captionText = "Путешественница", frameColor = "#FDFCF8") {
    const fontName = 'Caveat';
    
    // Load custom handwriting Google font for a realistic polaroid look
    if (!document.getElementById('traveler-font')) {
        const style = document.createElement('style');
        style.id = 'traveler-font';
        style.innerHTML = `@import url('https://fonts.googleapis.com/css2?family=Caveat:wght@700&display=swap');`;
        document.head.appendChild(style);
    }

    try {
        // Wait for font with a timeout fallback
        await Promise.race([
            document.fonts.load(`700 30px ${fontName}`),
            new Promise(resolve => setTimeout(resolve, 2000))
        ]);
    } catch (e) {
        // Fallback to defaults quietly
    }

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

    // Scale to a reasonable size ensuring good performance and readable text
    let scale = 1;
    const maxDim = 1000;
    if (originalImg.width > maxDim || originalImg.height > maxDim) {
        scale = maxDim / Math.max(originalImg.width, originalImg.height);
    }
    
    const imgW = originalImg.width * scale;
    const imgH = originalImg.height * scale;

    const mTop = Math.max(30, imgW * 0.06);
    const mSide = mTop;
    const mBot = Math.max(120, imgH * 0.25);

    canvas.width = imgW + mSide * 2;
    canvas.height = imgH + mTop + mBot;

    // Draw polaroid paper back
    ctx.fillStyle = frameColor;
    ctx.fillRect(0, 0, canvas.width, canvas.height);

    // Subtle edge shadowing
    ctx.strokeStyle = "rgba(0, 0, 0, 0.05)";
    ctx.lineWidth = 4;
    ctx.strokeRect(2, 2, canvas.width - 4, canvas.height - 4);

    // Draw the source user image
    ctx.drawImage(originalImg, mSide, mTop, imgW, imgH);

    // Outline separating photo from the frame
    ctx.strokeStyle = "rgba(0, 0, 0, 0.4)";
    ctx.lineWidth = 1;
    ctx.strokeRect(mSide, mTop, imgW, imgH);

    // --- 1. Draw Travel Postcard Stamp Overlay (Top Right) ---
    const stampX = mSide + imgW - (imgW * 0.08);
    const stampY = mTop + (imgW * 0.08);
    const stampRadius = Math.max(30, imgW * 0.06);

    ctx.save();
    ctx.translate(stampX, stampY);
    ctx.rotate(Math.PI / 7); 
    ctx.strokeStyle = "rgba(200, 50, 50, 0.75)"; 
    ctx.lineWidth = Math.max(2, stampRadius * 0.04);
    
    ctx.beginPath(); ctx.arc(0, 0, stampRadius, 0, Math.PI * 2); ctx.stroke();
    ctx.beginPath(); ctx.arc(0, 0, stampRadius * 0.75, 0, Math.PI * 2); ctx.stroke();
    
    ctx.font = `bold ${stampRadius * 0.22}px "Courier New", Courier, monospace`;
    ctx.fillStyle = "rgba(200, 50, 50, 0.75)";
    ctx.textAlign = "center";
    ctx.textBaseline = "middle";
    ctx.fillText("TRAVELER", 0, -stampRadius * 0.35);
    const dateStr = new Date().toLocaleDateString('en-GB', {day: '2-digit', month: 'short', year: 'numeric'}).toUpperCase();
    ctx.fillText(dateStr, 0, stampRadius * 0.35);

    ctx.restore();

    // Cancellation wavy lines
    ctx.save();
    ctx.translate(stampX - stampRadius * 1.5, stampY);
    ctx.rotate(Math.PI / 12);
    ctx.strokeStyle = "rgba(200, 50, 50, 0.6)";
    ctx.lineWidth = Math.max(1.5, stampRadius * 0.03);
    ctx.beginPath();
    for (let i = -2; i <= 2; i++) {
        const yLine = i * (stampRadius * 0.25);
        ctx.moveTo(0, yLine);
        for(let x = 0; x < stampRadius * 4; x += 5) {
            ctx.lineTo(x, yLine + Math.sin(x * 0.2) * (stampRadius * 0.15));
        }
    }
    ctx.stroke();
    ctx.restore();

    // --- 2. Draw Navigation Compass Overlay (Bottom Left) ---
    const compassX = mSide + (imgW * 0.1);
    const compassY = mTop + imgH - (imgH * 0.1);
    const compassR = Math.max(25, imgW * 0.07);

    ctx.save();
    ctx.translate(compassX, compassY);
    
    ctx.beginPath(); ctx.arc(0, 0, compassR * 1.1, 0, Math.PI * 2);
    ctx.fillStyle = 'rgba(255, 255, 255, 0.6)'; ctx.fill();
    
    ctx.beginPath(); ctx.arc(0, 0, compassR, 0, Math.PI * 2);
    ctx.strokeStyle = '#333'; ctx.lineWidth = compassR * 0.05; ctx.stroke();
    
    const drawPoint = (angle, length, width, color1, color2) => {
        ctx.save();
        ctx.rotate(angle);
        ctx.beginPath(); ctx.moveTo(0, -length); ctx.lineTo(width, 0); ctx.lineTo(0, width * 0.2);
        ctx.fillStyle = color1; ctx.fill();
        ctx.beginPath(); ctx.moveTo(0, -length); ctx.lineTo(-width, 0); ctx.lineTo(0, width * 0.2);
        ctx.fillStyle = color2; ctx.fill();
        ctx.restore();
    };
    
    for (let i = 0; i < 4; i++) {
        drawPoint((Math.PI/2) * i + Math.PI/4, compassR * 0.6, compassR * 0.15, '#666', '#999');
    }
    for (let i = 0; i < 4; i++) {
        const primary = i === 0 ? '#cc0000' : '#444'; // point north red
        const secondary = i === 0 ? '#ff4d4d' : '#777';
        drawPoint((Math.PI/2) * i, compassR * 0.95, compassR * 0.2, primary, secondary);
    }
    
    ctx.beginPath(); ctx.arc(0, 0, compassR * 0.15, 0, Math.PI * 2);
    ctx.fillStyle = '#fff'; ctx.fill();
    ctx.strokeStyle = '#333'; ctx.stroke();
    ctx.restore();

    // --- 3. Draw Travel Map Location Pin (Bottom Right) ---
    const pinX = mSide + imgW - (imgW * 0.1);
    const pinY = mTop + imgH + (imgH * 0.05); // Pinners point slightly below image border
    const pinScale = Math.max(0.5, (imgW * 0.05) / 20); 

    ctx.save();
    ctx.translate(pinX, pinY);
    ctx.scale(pinScale, pinScale);
    
    // Pin Drop shadow
    ctx.beginPath();
    ctx.ellipse(0, 0, 15, 5, 0, 0, Math.PI * 2);
    ctx.fillStyle = 'rgba(0,0,0,0.3)';
    ctx.fill();

    // Pin shape
    ctx.beginPath();
    ctx.moveTo(0, 0);
    ctx.bezierCurveTo(-15, -15, -25, -25, -25, -45);
    ctx.arc(0, -45, 25, Math.PI, Math.PI*2);
    ctx.bezierCurveTo(25, -25, 15, -15, 0, 0);
    ctx.fillStyle = '#e63946'; // Red pin color
    ctx.fill();
    ctx.lineWidth = 2;
    ctx.strokeStyle = '#a8dadc';
    ctx.stroke();
    
    // Pin hollow center
    ctx.beginPath();
    ctx.arc(0, -45, 10, 0, Math.PI*2);
    ctx.fillStyle = '#f1faee';
    ctx.fill();
    ctx.restore();

    // --- 4. Draw Handwriting Caption at the Bottom ---
    let isFontLoaded = document.fonts.check(`700 30px ${fontName}`);
    let fontFamily = isFontLoaded ? `'${fontName}', cursive` : `'Comic Sans MS', cursive, sans-serif`;
    
    let fontSize = Math.max(30, mBot * 0.4);
    ctx.font = `700 ${fontSize}px ${fontFamily}`;
    
    // Scale down text if it's too wide
    let textMetrics = ctx.measureText(captionText);
    if (textMetrics.width > canvas.width * 0.85) {
        fontSize = fontSize * ((canvas.width * 0.85) / textMetrics.width);
        ctx.font = `700 ${fontSize}px ${fontFamily}`;
    }

    ctx.fillStyle = "#1d3557"; // Deep navy ink
    ctx.textAlign = 'center';
    ctx.textBaseline = 'middle';
    
    const textX = canvas.width / 2;
    const textY = mTop + imgH + (mBot / 2);

    ctx.save();
    ctx.translate(textX, textY);
    ctx.rotate(-Math.PI / 180); // Rotate roughly -1 degree for organic hand written look
    ctx.fillText(captionText, 0, 0);
    ctx.restore();

    // --- 5. Add universal film texture/grain ---
    try {
        const imgData = ctx.getImageData(0, 0, canvas.width, canvas.height);
        const data = imgData.data;
        for (let i = 0; i < data.length; i += 4) {
            const noise = (Math.random() - 0.5) * 8; // Slight perturbation
            data[i] = Math.min(255, Math.max(0, data[i] + noise));
            data[i+1] = Math.min(255, Math.max(0, data[i+1] + noise));
            data[i+2] = Math.min(255, Math.max(0, data[i+2] + noise));
        }
        ctx.putImageData(imgData, 0, 0);
    } catch(e) {
        // Proceed silently if cross-origin policy prevents pixel graining, everything else drew fine!
    }

    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 Traveler Creator is a creative tool that transforms your standard photos into vintage-style Polaroid snapshots with a travel theme. It automatically applies a classic white frame, decorative overlays including a travel postcard stamp with a date, a navigation compass, and a location pin, and adds a realistic film grain texture. Users can also add custom handwritten-style captions to the bottom of the frame. This tool is perfect for travelers looking to enhance their vacation photos for social media, creating digital scrapbooks, or designing themed postcards and memories.

Leave a Reply

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