Please bookmark this page to avoid losing your image tool!

Image Evidence File Creator For Ghost Hunters

(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,
    caseNumber = "CASE #001",
    location = "UNKNOWN LOCATION",
    customNotes = "PARANORMAL ACTIVITY SUSPECTED",
    textColor = "rgba(0, 255, 0, 0.85)",
    fontFamily = "Consolas, 'Courier New', monospace",
    fontSize = 16,
    showRecSymbol = 1,
    recSymbolColor = "rgba(255, 0, 0, 0.9)",
    applySpookyFilter = 1,
    vignetteIntensity = 0.6
) {
    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');

    // Use naturalWidth/Height if available, fallback to width/height.
    // These might be 0 if the image isn't loaded yet or is invalid.
    const imgWidth = originalImg.naturalWidth || originalImg.width;
    const imgHeight = originalImg.naturalHeight || originalImg.height;

    // Handle cases where the image might not be loaded or is invalid
    if (imgWidth === 0 || imgHeight === 0) {
        console.error("Image not loaded or has zero dimensions. Ensure originalImg is a fully loaded HTMLImageElement.");
        // Return a small canvas with an error message
        canvas.width = 250;
        canvas.height = 50;
        ctx.fillStyle = 'rgba(128, 0, 0, 0.8)'; // Dark red
        ctx.fillRect(0,0,canvas.width, canvas.height);
        ctx.fillStyle = 'white';
        ctx.font = '12px Arial';
        ctx.textAlign = 'center';
        ctx.textBaseline = 'middle';
        ctx.fillText("Error: Image data not available or invalid.", canvas.width/2, canvas.height/2);
        return canvas;
    }

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

    // 1. Apply base image filter if requested
    if (applySpookyFilter === 1) {
        // This filter gives a "security camera" or "night vision footage" feel
        ctx.filter = 'grayscale(60%) contrast(130%) brightness(85%)';
    }
    ctx.drawImage(originalImg, 0, 0, canvas.width, canvas.height);
    ctx.filter = 'none'; // Reset filter for any subsequent drawings on the canvas

    // 2. Apply vignette if requested and intensity is positive
    // VignetteIntensity should be between 0 (no vignette) and 1 (max vignette)
    const effectiveVignetteIntensity = Math.max(0, Math.min(1, vignetteIntensity));
    if (applySpookyFilter === 1 && effectiveVignetteIntensity > 0) {
        const centerX = canvas.width / 2;
        const centerY = canvas.height / 2;
        
        // Inner radius of the vignette's clear area
        const innerRadius = Math.min(canvas.width, canvas.height) * 0.2; // Smaller clear center for more drama
        // Outer radius where the vignette is most opaque
        const outerRadius = Math.sqrt(Math.pow(centerX, 2) + Math.pow(centerY, 2)); // Covers up to the corners

        const gradient = ctx.createRadialGradient(
            centerX, centerY, innerRadius,
            centerX, centerY, outerRadius
        );

        // Vignette fades from transparent to dark
        gradient.addColorStop(0, 'rgba(0,0,0,0)'); // Transparent at innerRadius
        // Control point for smoother or sharper fade
        gradient.addColorStop(0.65, `rgba(0,0,0,${(effectiveVignetteIntensity * 0.4).toFixed(2)})`); 
        gradient.addColorStop(1, `rgba(0,0,0,${effectiveVignetteIntensity.toFixed(2)})`); // Full intensity at outerRadius
        
        ctx.fillStyle = gradient;
        ctx.fillRect(0, 0, canvas.width, canvas.height);
    }

    // Text and Symbol settings
    const safeFontSize = Math.max(8, fontSize); // Ensure font size is reasonably usable
    const padding = Math.max(8, Math.round(safeFontSize * 0.6)); 
    
    ctx.font = `${safeFontSize}px ${fontFamily}`;
    ctx.fillStyle = textColor;

    // --- Top Text Elements ---
    ctx.textBaseline = 'top'; // Align text from its top edge

    // REC Symbol and Evidence Text (Top-left)
    let currentXDrawPos = padding; // X-coordinate for drawing elements
    if (showRecSymbol === 1) {
        const recCircleRadius = Math.round(safeFontSize * 0.45);
        const recCircleCenterY = padding + recCircleRadius; 
        const recCircleCenterX = currentXDrawPos + recCircleRadius;

        ctx.beginPath();
        ctx.arc(recCircleCenterX, recCircleCenterY, recCircleRadius, 0, Math.PI * 2);
        ctx.fillStyle = recSymbolColor;
        ctx.fill();

        // "REC" text inside the circle, if space allows
        const recTextSize = Math.round(recCircleRadius * 1.1); // Font size for "REC"
        if (recTextSize >= 6) { // Only draw if potentially legible
            ctx.font = `bold ${recTextSize}px ${fontFamily}`;
            ctx.fillStyle = 'white'; 
            ctx.textAlign = 'center';
            ctx.textBaseline = 'middle'; 
            ctx.fillText("REC", recCircleCenterX, recCircleCenterY);
        }
        
        currentXDrawPos += recCircleRadius * 2 + Math.round(padding / 1.5); // Advance X for evidence text
        
        // Reset font settings for the main evidence text
        ctx.font = `${safeFontSize}px ${fontFamily}`;
        ctx.fillStyle = textColor;
        ctx.textAlign = 'left'; 
        ctx.textBaseline = 'top'; 
    }

    const evidenceText = `EVIDENCE: ${caseNumber}`;
    ctx.fillText(evidenceText, currentXDrawPos, padding);


    // Timestamp (Top-right)
    const now = new Date();
    const timestamp = `${String(now.getFullYear())}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')} ${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}:${String(now.getSeconds()).padStart(2, '0')}`;
    ctx.textAlign = 'right';
    ctx.fillText(timestamp, canvas.width - padding, padding);

    // --- Bottom Text Elements ---
    ctx.textBaseline = 'bottom'; // Align text from its bottom edge for easier placement at canvas bottom
    ctx.font = `${safeFontSize}px ${fontFamily}`; // Ensure font is set
    ctx.fillStyle = textColor;

    const bottomTextY = canvas.height - padding; // Y-coordinate for the baseline of bottom-most text

    // Measure text widths to decide on layout (side-by-side or stacked)
    ctx.textAlign = 'left'; // Set for measuring location string
    const locationWidth = ctx.measureText(location).width;
    
    // For customNotes, measure as if it were drawn from the right
    // (ctx.measureText assumes left-alignment, so this will correctly give its width)
    const notesWidth = ctx.measureText(customNotes).width;
    
    const spaceBetweenBottomTexts = padding * 2; // Required space between location and notes if side-by-side

    // Check if location and customNotes can fit on one line without overlapping
    if (padding + locationWidth + spaceBetweenBottomTexts + notesWidth + padding <= canvas.width) {
        // Texts fit side-by-side
        ctx.textAlign = 'left';
        ctx.fillText(location, padding, bottomTextY);
        ctx.textAlign = 'right';
        ctx.fillText(customNotes, canvas.width - padding, bottomTextY);
    } else {
        // Texts are too long; stack them (Location above Notes, both left-aligned)
        ctx.textAlign = 'left';
        const textLineHeightApproximation = safeFontSize * 1.2; // Common estimate for line height
        ctx.fillText(location, padding, bottomTextY - textLineHeightApproximation); 
        ctx.fillText(customNotes, padding, bottomTextY); 
    }

    // Reset canvas context properties that were changed, to be a good citizen
    ctx.textAlign = 'left';
    ctx.textBaseline = 'alphabetic'; // Canvas default baseline
    ctx.filter = 'none'; // Ensure filter is off

    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 Evidence File Creator for Ghost Hunters allows users to create professionally annotated images tailored for paranormal investigations. By uploading an original image, users can add key details such as case number, location, and notes about the paranormal activity. This tool enhances images with visual effects, including a spooky filter and vignette, to give them a unique presentation. It integrates timestamps and custom annotations, making it ideal for ghost hunters, investigators, and enthusiasts looking to document their findings visually. It’s perfect for creating evidence files, reports, or sharing compelling images on social media.

Leave a Reply

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