Please bookmark this page to avoid losing your image tool!

Image Pirate Treasure Map Frame 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.
function processImage(originalImg, frameThickness = 50, baseParchmentColor = "rgb(240,225,190)", burnEdgeColor = "rgb(100,60,20)", cornerSymbol = "skull", decorationColor = "rgb(60,40,20)") {
    const ft = Math.max(20, frameThickness); // Ensure a minimum thickness

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

    // Helper: Random number in range
    function rand(min, max) {
        return Math.random() * (max - min) + min;
    }

    // Helper function to draw a jagged line segment for the parchment path
    function addJaggedLineSegment(ctx, p1, p2, jMagnitude, numSegments) {
        const dx = p2.x - p1.x;
        const dy = p2.y - p1.y;
        const segLen = Math.sqrt(dx * dx + dy * dy);
        if (segLen === 0) {
            ctx.lineTo(p2.x, p2.y);
            return;
        }
        const unitDx = dx / segLen;
        const unitDy = dy / segLen;
        const perpX = -unitDy; // Perpendicular vector component
        const perpY = unitDx;  // Perpendicular vector component

        for (let i = 1; i < numSegments; i++) {
            const t = i / numSegments;
            const currentX = p1.x + t * dx;
            const currentY = p1.y + t * dy;
            let displacement = (Math.random() - 0.5) * 2 * jMagnitude;
            // Reduce jaggedness near endpoints for smoother transitions if needed, but full randomness is fine for torn paper
            if (i < numSegments * 0.1 || i > numSegments * 0.9) {
                 displacement *= 0.5; // Optional: taper near ends
            }
            ctx.lineTo(currentX + displacement * perpX, currentY + displacement * perpY);
        }
        ctx.lineTo(p2.x, p2.y); // Ensure it ends at the exact corner
    }

    // 1. Draw Burnt Background
    ctx.fillStyle = burnEdgeColor;
    ctx.fillRect(0, 0, canvas.width, canvas.height);

    // 2. Draw Jagged Parchment Layer
    const parchmentPath = new Path2D();
    const nominalBorder = ft * 0.05; // Nominal small border of "burn" visible
    const pX = nominalBorder;
    const pY = nominalBorder;
    const pW = canvas.width - 2 * nominalBorder;
    const pH = canvas.height - 2 * nominalBorder;
    const jAmount = ft * 0.2; // Jaggedness magnitude
    const pointsPerSide = 15;

    // Define perturbed corners for the parchment
    const cTL = { x: pX + rand(-jAmount * 0.3, jAmount * 0.3), y: pY + rand(-jAmount * 0.3, jAmount * 0.3) };
    const cTR = { x: pX + pW + rand(-jAmount * 0.3, jAmount * 0.3), y: pY + rand(-jAmount * 0.3, jAmount * 0.3) };
    const cBR = { x: pX + pW + rand(-jAmount * 0.3, jAmount * 0.3), y: pY + pH + rand(-jAmount * 0.3, jAmount * 0.3) };
    const cBL = { x: pX + rand(-jAmount * 0.3, jAmount * 0.3), y: pY + pH + rand(-jAmount * 0.3, jAmount * 0.3) };

    parchmentPath.moveTo(cTL.x, cTL.y);
    addJaggedLineSegment(parchmentPath, cTL, cTR, jAmount, pointsPerSide);
    addJaggedLineSegment(parchmentPath, cTR, cBR, jAmount, pointsPerSide);
    addJaggedLineSegment(parchmentPath, cBR, cBL, jAmount, pointsPerSide);
    addJaggedLineSegment(parchmentPath, cBL, cTL, jAmount, pointsPerSide);
    parchmentPath.closePath();

    ctx.fillStyle = baseParchmentColor;
    ctx.fill(parchmentPath);

    // 3. Add subtle speckle texture to parchment
    ctx.save();
    ctx.clip(parchmentPath);
    const numSpeckles = (canvas.width * canvas.height) / 70; // Adjust density
    for (let i = 0; i < numSpeckles; i++) {
        const x = Math.random() * canvas.width;
        const y = Math.random() * canvas.height;
        // Use a slightly darker, semi-transparent black for speckles
        ctx.fillStyle = `rgba(0, 0, 0, ${Math.random() * 0.08 + 0.02})`;
        ctx.beginPath();
        ctx.arc(x, y, Math.random() * 1.5 + 0.5, 0, Math.PI * 2);
        ctx.fill();
    }
    ctx.restore(); // Remove clipping

    // 4. Draw the Original Image
    ctx.drawImage(originalImg, ft, ft, originalImg.width, originalImg.height);

    // 5. Draw Inner Shadow/Border for Image (subtle)
    ctx.strokeStyle = 'rgba(0,0,0,0.25)';
    ctx.lineWidth = 1;
    ctx.strokeRect(ft - 0.5, ft - 0.5, originalImg.width + 1, originalImg.height + 1);
    ctx.strokeStyle = 'rgba(0,0,0,0.15)';
    ctx.lineWidth = 2;
    ctx.strokeRect(ft - 1.5, ft - 1.5, originalImg.width + 3, originalImg.height + 3);


    // --- Corner Decoration Functions ---
    function drawSkull(ctx, cx, cy, size, color) {
        ctx.fillStyle = color;
        const headRadiusX = size * 0.25;
        const headRadiusY = size * 0.3;
        const eyeRadius = size * 0.07;
        const eyeOffsetX = size * 0.11;
        const eyeOffsetY = -size * 0.08;
        const noseSize = size * 0.05;

        // Head shape
        ctx.beginPath();
        ctx.ellipse(cx, cy, headRadiusX, headRadiusY, 0, 0, Math.PI * 2);
        ctx.fill();

        // Eyes (simple dark circles)
        const eyeColor = "black";
        ctx.fillStyle = eyeColor;
        ctx.beginPath();
        ctx.arc(cx - eyeOffsetX, cy + eyeOffsetY, eyeRadius, 0, Math.PI * 2);
        ctx.fill();
        ctx.beginPath();
        ctx.arc(cx + eyeOffsetX, cy + eyeOffsetY, eyeRadius, 0, Math.PI * 2);
        ctx.fill();

        // Nose (simple triangle)
        ctx.beginPath();
        ctx.moveTo(cx, cy + size * 0.04);
        ctx.lineTo(cx - noseSize, cy + size * 0.13);
        ctx.lineTo(cx + noseSize, cy + size * 0.13);
        ctx.closePath();
        ctx.fill();
    }

    function drawCrossbones(ctx, cx, cy, size, color) {
        ctx.fillStyle = color;
        ctx.strokeStyle = color; // If outlining parts not filled
        ctx.lineWidth = size * 0.03;

        const boneLength = size * 0.7;
        const boneWidth = size * 0.12;
        const endRadius = boneWidth * 0.6;

        ctx.save();
        ctx.translate(cx, cy);

        for (let i = 0; i < 2; i++) {
            ctx.rotate(i === 0 ? Math.PI / 4 : Math.PI / 2); // Rotate for first, then additional 90 for second

            // Bone shaft
            ctx.beginPath();
            ctx.rect(-boneLength / 2, -boneWidth / 2, boneLength, boneWidth);
            ctx.fill();
            
            // Bone ends (simplified as circles)
            ctx.beginPath();
            ctx.arc(-boneLength / 2 - endRadius*0.1, 0, endRadius, 0, Math.PI * 2);
            ctx.fill();
            ctx.beginPath();
            ctx.arc(boneLength / 2 + endRadius*0.1, 0, endRadius, 0, Math.PI * 2);
            ctx.fill();
        }
        ctx.restore();
    }

    function drawXMark(ctx, cx, cy, size, color) {
        ctx.strokeStyle = color;
        ctx.lineWidth = Math.max(2, size * 0.15); // Ensure visible line
        ctx.lineCap = 'round';
        const arm = size * 0.35;
        ctx.beginPath();
        ctx.moveTo(cx - arm, cy - arm);
        ctx.lineTo(cx + arm, cy + arm);
        ctx.moveTo(cx + arm, cy - arm);
        ctx.lineTo(cx - arm, cy + arm);
        ctx.stroke();
    }

    // 6. Draw Corner Decorations
    const decSymbol = cornerSymbol.toLowerCase();
    if (decSymbol !== "none") {
        const decSize = ft * 0.65; // Size of the decoration
        const decAnchorOffset = ft * 0.5; // Center of decoration from edge of canvas

        const corners = [
            { x: decAnchorOffset, y: decAnchorOffset }, // TL
            { x: canvas.width - decAnchorOffset, y: decAnchorOffset }, // TR
            { x: decAnchorOffset, y: canvas.height - decAnchorOffset }, // BL
            { x: canvas.width - decAnchorOffset, y: canvas.height - decAnchorOffset } // BR
        ];

        corners.forEach(corner => {
            if (decSymbol === "skull") {
                drawSkull(ctx, corner.x, corner.y, decSize, decorationColor);
            } else if (decSymbol === "crossbones") {
                drawCrossbones(ctx, corner.x, corner.y, decSize, decorationColor);
            } else if (decSymbol === "x") {
                drawXMark(ctx, corner.x, corner.y, decSize, decorationColor);
            }
        });
    }
    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 Pirate Treasure Map Frame Creator is a web-based tool that allows users to create decorative frames around their images, resembling old pirate maps. Users can customize several aspects of the frame, including thickness, parchment color, edge burning effects, and corner symbols such as skulls or crossbones. This tool is ideal for creating themed invitations, artwork, or playful web content that captures a maritime adventure aesthetic. Whether for personal projects, party decorations, or digital storytelling, this tool enhances images with unique and stylish borders.

Leave a Reply

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