Please bookmark this page to avoid losing your image tool!

Best Friend Image 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, text = "Лучший друг", textColor = "#333333", frameColor = "#F9F6F0", heartColor = "#E63946") {
    // Inject and load a clean handwritten font (Caveat supports Cyrillic chars gracefully)
    const fontName = 'Caveat';
    if (!document.getElementById('font-caveat')) {
        const link = document.createElement('link');
        link.id = 'font-caveat';
        link.rel = 'stylesheet';
        link.href = `https://fonts.googleapis.com/css2?family=${fontName}:wght@700&display=swap`;
        document.head.appendChild(link);
    }

    try {
        await document.fonts.ready;
        await document.fonts.load(`700 20px "${fontName}"`);
    } catch (e) {
        console.warn("Font loading failed, falling back to safe alternatives.");
    }

    // Determine dimensions recursively based on the actual image sizes
    const w = originalImg.naturalWidth;
    const h = originalImg.naturalHeight;

    const maxDim = Math.max(w, h);
    const padding = Math.max(maxDim * 0.05, 20);
    const bottomPadding = Math.max(maxDim * 0.20, 80);

    const cw = w + padding * 2;
    const ch = h + padding + bottomPadding;

    const canvas = document.createElement("canvas");
    canvas.width = cw;
    canvas.height = ch;
    const ctx = canvas.getContext("2d");

    // 1. Draw polaroid frame wrapper
    ctx.fillStyle = frameColor;
    ctx.fillRect(0, 0, cw, ch);

    // Subtle edge framing outline
    ctx.strokeStyle = "#e8e5df";
    ctx.lineWidth = Math.max(maxDim * 0.005, 2);
    ctx.strokeRect(ctx.lineWidth / 2, ctx.lineWidth / 2, cw - ctx.lineWidth, ch - ctx.lineWidth);

    // 2. Draw slight dark inset behind image
    ctx.fillStyle = "#d0d0d0";
    ctx.fillRect(padding - 2, padding - 2, w + 4, h + 4);

    // 3. Draw the original image
    ctx.drawImage(originalImg, padding, padding, w, h);

    // 4. Draw a scrapbook adhesive tape at the top
    ctx.save();
    ctx.translate(cw / 2, padding);
    ctx.rotate(-0.02); // Playful tilt
    ctx.fillStyle = "rgba(255, 255, 255, 0.45)"; // Semi-transparent, cloudy white
    ctx.shadowColor = "rgba(0, 0, 0, 0.15)";
    ctx.shadowBlur = 5;
    ctx.shadowOffsetY = 2;

    const tapeW = Math.max(maxDim * 0.25, 80);
    const tapeH = Math.max(padding * 0.5, 15);

    ctx.fillRect(-tapeW / 2, -tapeH / 2, tapeW, tapeH);

    // Draw little tape 'creases'
    ctx.shadowColor = "transparent";
    ctx.fillStyle = "rgba(255, 255, 255, 0.55)";
    ctx.fillRect(-tapeW / 2 + tapeW * 0.05, -tapeH / 2, tapeW * 0.02, tapeH);
    ctx.fillRect(tapeW / 2 - tapeW * 0.08, -tapeH / 2, tapeW * 0.02, tapeH);
    ctx.restore();

    // 5. Setup text properties
    let fontSize = Math.max(bottomPadding * 0.35, 16);
    ctx.font = `700 ${fontSize}px "${fontName}", "Comic Sans MS", cursive`;
    
    // Evaluate combined Text + Heart footprint to fit it nicely with shrink-to-fit logic
    const heartSize = fontSize * 0.75; 
    const heartWidth = heartSize * 2;
    const maxAllowedWidth = cw - padding * 2;

    let textMetrics = ctx.measureText(text);
    let textWidth = textMetrics.width;
    let gap = textWidth > 0 ? fontSize * 0.3 : 0;
    
    let totalWidth = textWidth + gap + heartWidth;

    // Shrink if needed (useful for extremely long names / inputs)
    if (totalWidth > maxAllowedWidth) {
        const scaleFact = maxAllowedWidth / totalWidth;
        fontSize *= scaleFact;
        ctx.font = `700 ${fontSize}px "${fontName}", "Comic Sans MS", cursive`;
        textMetrics = ctx.measureText(text);
        textWidth = textMetrics.width;
        gap = textWidth > 0 ? fontSize * 0.3 : 0;
        totalWidth = textWidth + gap + (fontSize * 0.75 * 2);
    }
    
    // 6. Draw handwritten text + Heart
    const textY = h + padding + bottomPadding * 0.45;
    
    ctx.save();
    ctx.translate(cw / 2, textY);
    ctx.rotate(-0.015); // Authentic handwritten slight slant applied globally to text + heart

    const startX = -totalWidth / 2;
    const textCenterX = startX + textWidth / 2;
    const finalHeartSize = fontSize * 0.75; 
    const heartCenterX = startX + textWidth + gap + finalHeartSize;

    // Soft ink pooling / shadow memory effect for visual charm
    ctx.shadowColor = "rgba(0, 0, 0, 0.15)";
    ctx.shadowBlur = 2;
    ctx.shadowOffsetX = 1;
    ctx.shadowOffsetY = 1;

    // Draw text
    if (textWidth > 0) {
        ctx.textAlign = "center";
        ctx.textBaseline = "middle";
        ctx.fillStyle = textColor;
        ctx.fillText(text, textCenterX, 0);
    }

    // Draw heart
    ctx.translate(heartCenterX, -fontSize * 0.05); // Vertically align the heart with text
    
    ctx.beginPath();
    ctx.moveTo(0, finalHeartSize * 0.25);
    ctx.bezierCurveTo(-finalHeartSize / 2, -finalHeartSize * 0.25, -finalHeartSize, finalHeartSize * 0.4, 0, finalHeartSize);
    ctx.bezierCurveTo(finalHeartSize, finalHeartSize * 0.4, finalHeartSize / 2, -finalHeartSize * 0.25, 0, finalHeartSize * 0.25);

    ctx.fillStyle = heartColor;
    ctx.fill();

    ctx.restore();

    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 Best Friend Image Tool allows you to transform your photos into nostalgic, scrapbook-style Polaroid images. The tool adds a custom frame, a playful adhesive tape effect at the top, and a handwritten-style caption accompanied by a heart icon. Users can customize the text, text color, frame color, and heart color to create personalized keepsakes. This tool is ideal for creating heartfelt digital gifts, social media posts for special occasions, or aesthetic scrapbooking content to celebrate friendships and memories.

Leave a Reply

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