Please bookmark this page to avoid losing your image tool!

Image Pirate Ship’s Log 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,
                            title = "Captain's Log",
                            entryDate = "17th Day o' Bad Luck, Anno 1742",
                            textColor = "#3B2F2F", // Dark, slightly desaturated brown for text
                            titleFontName = "Pirata One", // Pirate-style font
                            entryFontName = "Georgia",    // Standard serif for readability
                            titleFontSizeFactor = 0.08, // Factor of image height for title font size
                            entryFontSizeFactor = 0.03, // Factor of image height for entry font size
                            sepiaAmount = 0.75,       // Sepia effect intensity (0 to 1)
                            vignetteColor = "rgba(30,15,5,1)", // Base color for vignette (e.g., dark warm brown, alpha is ignored here)
                            vignetteOuterAlpha = 0.8, // Alpha of the vignette color at the extreme edges (0 to 1)
                            vignetteInnerRadiusFactor = 0.25, // Start of vignette transparency (0=center, 1=edge) relative to image diagonal
                            vignetteOuterRadiusFactor = 0.9,  // End of vignette transparency (0=center, 1=edge) relative to image diagonal
                            parchmentBaseColor = "#F0E8D8", // Light beige/parchment color for background tint
                            parchmentImageBlendOpacity = 0.7 // Opacity of the image when drawn over parchment (0 to 1)
                           ) {

    // --- Font loading helper ---
    async function loadWebFont(fontName, fontUrl) {
        // Check if font is already loaded or available in the document's font set
        if (document.fonts.check(`12px "${fontName}"`)) {
            // console.log(`Font "${fontName}" is already available.`);
            return true; // Indicates font is ready or was already loaded
        }
        try {
            const fontFace = new FontFace(fontName, `url(${fontUrl})`);
            await fontFace.load(); // Wait for the font to be fetched and ready
            document.fonts.add(fontFace); // Add it to the document's font set for use
            // console.log(`Font "${fontName}" loaded and added successfully from ${fontUrl}.`);
            return true;
        } catch (e) {
            console.warn(`Font "${fontName}" from URL "${fontUrl}" failed to load. System fallback will be used. Error:`, e);
            return false; // Indicates font loading failed
        }
    }

    // --- Load specified web fonts ---
    // Example: Load "Pirata One" if specified for the title
    if (titleFontName === "Pirata One") {
        await loadWebFont("Pirata One", "https://fonts.gstatic.com/s/pirataone/v21/I_urMpiDvgLWTsN3iQCXh_hU510_I8c.woff2");
    }
    // Add similar blocks here if entryFontName or other fonts are web fonts needing dynamic loading.


    // --- Canvas and Context Setup ---
    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');

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

    // Handle invalid image dimensions (e.g., image not loaded yet)
    if (!imgWidth || !imgHeight) {
        console.error("Image dimensions are invalid (e.g., 0x0). Make sure the image is fully loaded before processing.");
        // Return a small canvas with an error message
        canvas.width = 250;
        canvas.height = 100;
        ctx.fillStyle = "#FDD"; // Light red background for error
        ctx.fillRect(0,0,canvas.width, canvas.height);
        ctx.fillStyle = "red";
        ctx.font = "12px Arial";
        ctx.textAlign = "center";
        ctx.textBaseline = "middle";
        ctx.fillText("Error: Invalid image dimensions.", canvas.width/2, canvas.height/2 -10);
        ctx.fillText("Please ensure the image is loaded.", canvas.width/2, canvas.height/2 + 10);
        return canvas;
    }

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

    // --- 1. Draw Parchment Background Tint ---
    // If a parchment color is specified, fill the canvas with it first.
    if (parchmentBaseColor) {
        ctx.fillStyle = parchmentBaseColor;
        ctx.fillRect(0, 0, canvas.width, canvas.height);
    }

    // --- 2. Draw the Original Image ---
    // Adjust image opacity if blending with parchment background.
    const finalImageOpacity = parchmentBaseColor ? Math.max(0, Math.min(1, parchmentImageBlendOpacity)) : 1.0;
    if (finalImageOpacity < 1.0) {
        ctx.globalAlpha = finalImageOpacity;
    }
    ctx.drawImage(originalImg, 0, 0, canvas.width, canvas.height);
    if (finalImageOpacity < 1.0) {
        ctx.globalAlpha = 1.0; // Reset globalAlpha for subsequent operations
    }

    // --- 3. Apply Sepia Filter ---
    if (sepiaAmount > 0) {
        const sAmount = Math.max(0, Math.min(1, sepiaAmount)); // Clamp sepiaAmount to [0, 1]
        const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
        const data = imageData.data;
        for (let i = 0; i < data.length; i += 4) {
            const r = data[i];
            const g = data[i + 1];
            const b = data[i + 2];

            // Standard sepia weights
            const sr = Math.min(255, (r * 0.393) + (g * 0.769) + (b * 0.189));
            const sg = Math.min(255, (r * 0.349) + (g * 0.686) + (b * 0.168));
            const sb = Math.min(255, (r * 0.272) + (g * 0.534) + (b * 0.131));

            // Blend original with sepia based on sAmount
            data[i]   = Math.round((1 - sAmount) * r + sAmount * sr);
            data[i+1] = Math.round((1 - sAmount) * g + sAmount * sg);
            data[i+2] = Math.round((1 - sAmount) * b + sAmount * sb);
        }
        ctx.putImageData(imageData, 0, 0);
    }

    // --- 4. Apply Vignette Effect ---
    const vOuterAlpha = Math.max(0, Math.min(1, vignetteOuterAlpha));
    const vInnerRFactor = Math.max(0, Math.min(1, vignetteInnerRadiusFactor));
    // Ensure outer radius factor is not less than inner, and clamp to [0,1]
    const vOuterRFactor = Math.max(vInnerRFactor, Math.min(1, vignetteOuterRadiusFactor));

    if (vOuterAlpha > 0 && vOuterRFactor > vInnerRFactor && vignetteColor) {
        ctx.save();
        const centerX = canvas.width / 2;
        const centerY = canvas.height / 2;
        // Calculate radius to a corner of the canvas, for vignette scaling
        const maxCanvasRadius = Math.hypot(centerX, centerY); 

        const r0_pixels = maxCanvasRadius * vInnerRFactor; // Inner radius of gradient (fully transparent)
        const r1_pixels = maxCanvasRadius * vOuterRFactor; // Outer radius of gradient (reaches vOuterAlpha)

        const gradient = ctx.createRadialGradient(centerX, centerY, r0_pixels, centerX, centerY, r1_pixels);

        // Parse the vignetteColor to get RGB components
        let baseR = 0, baseG = 0, baseB = 0;
        const colorMatch = vignetteColor.match(/rgba?\((\d+)\s*,\s*(\d+)\s*,\s*(\d+)(?:,\s*[\d.]+)?\)/);
        if (colorMatch) {
            baseR = parseInt(colorMatch[1]);
            baseG = parseInt(colorMatch[2]);
            baseB = parseInt(colorMatch[3]);
        } else {
            console.warn(`Could not parse vignetteColor "${vignetteColor}". Vignette may not appear as intended.`);
            // Default to black components if parsing fails, alpha is handled by vOuterAlpha
        }
        
        gradient.addColorStop(0, `rgba(${baseR},${baseG},${baseB},0)`); // Center of vignette is transparent
        gradient.addColorStop(1, `rgba(${baseR},${baseG},${baseB},${vOuterAlpha})`); // Edge of vignette uses vOuterAlpha

        ctx.fillStyle = gradient;
        ctx.fillRect(0, 0, canvas.width, canvas.height); // Apply gradient over the entire canvas
        ctx.restore();
    }

    // --- 5. Draw Text Elements (Title and Entry Date) ---
    // Calculate font sizes and margin based on image height, with minimums
    const actualTitleFontSize = Math.max(10, Math.round(canvas.height * Math.max(0, titleFontSizeFactor)));
    const actualEntryFontSize = Math.max(8, Math.round(canvas.height * Math.max(0, entryFontSizeFactor)));
    const margin = Math.max(5, Math.round(canvas.height * 0.04)); // 4% margin from edges, min 5px

    ctx.fillStyle = textColor;
    ctx.textBaseline = 'top'; // Align text from its top edge for easier Y positioning

    // Draw Title
    if (title && actualTitleFontSize > 0) {
        ctx.font = `${actualTitleFontSize}px "${titleFontName}", serif`; // Fallback to generic serif
        ctx.textAlign = 'center';
        ctx.fillText(title, canvas.width / 2, margin);
    }

    // Draw Entry Date
    if (entryDate && actualEntryFontSize > 0) {
        ctx.font = `${actualEntryFontSize}px "${entryFontName}", serif`; // Fallback to generic serif
        ctx.textAlign = 'right';
        // Position from bottom-right corner, considering font height for Y
        ctx.fillText(entryDate, canvas.width - margin, canvas.height - margin - actualEntryFontSize);
    }

    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 Ship’s Log Creator is a creative tool that allows users to transform an image into a pirate-themed log entry. This tool enables users to add a custom title and entry date using stylized fonts, apply a sepia effect for a vintage appearance, and incorporate a vignette and parchment background for added aesthetic appeal. It’s ideal for creating themed graphics, such as pirate adventure stories, party invitations, or engaging social media content. The tool is designed for easy use, allowing users to enhance their images with a whimsical, adventurous touch.

Leave a Reply

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