Please bookmark this page to avoid losing your image tool!

Soul Collector Case Image Viewer

(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, 
    theme = "dark", 
    intensity = 0.8, 
    stampText = "ДЕЛО О СБОРЩИКЕ ДУШ - СЕКРЕТНО", 
    soulCount = 7
) {
    // Ensure numeric values for intensity and soulCount
    const params = {
        theme: typeof theme === 'string' ? theme.toLowerCase() : "dark",
        intensity: parseFloat(intensity) || 0.8,
        stampText: String(stampText),
        soulCount: parseInt(soulCount, 10) || 7
    };

    // Create main canvas
    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');
    
    // Match dimensions to original image
    canvas.width = originalImg.width;
    canvas.height = originalImg.height;
    
    // Apply thematic CSS filters to the drawing context for the base image
    if (params.theme === "sepia") {
        // Old, degraded detective photo look
        ctx.filter = "sepia(100%) contrast(140%) brightness(60%) hue-rotate(-10deg)";
    } else {
        // Dark, noir, eerie look
        ctx.filter = "grayscale(100%) contrast(150%) brightness(40%)";
    }
    
    // Draw base image
    ctx.drawImage(originalImg, 0, 0);
    ctx.filter = "none"; // Reset filter for subsequent drawings

    // 1. Draw Vignette (Heavy dark edges concentrating vision to the center)
    const cx = canvas.width / 2;
    const cy = canvas.height / 2;
    const maxRadius = Math.max(canvas.width, canvas.height) * 0.8;
    
    const vignetteOffset = 0.3 - (params.intensity * 0.1); // Dynamic inner radius based on intensity
    const vignetteGrad = ctx.createRadialGradient(cx, cy, maxRadius * Math.max(0, vignetteOffset), cx, cy, maxRadius);
    
    vignetteGrad.addColorStop(0, "rgba(0, 0, 0, 0)");
    vignetteGrad.addColorStop(1, `rgba(0, 0, 0, ${Math.min(1, params.intensity + 0.1)})`);
    
    ctx.fillStyle = vignetteGrad;
    ctx.fillRect(0, 0, canvas.width, canvas.height);

    // 2. Add film grain/noise (to make it look like an old file or supernatural interference)
    const noiseSize = 150;
    const noiseCanvas = document.createElement('canvas');
    noiseCanvas.width = noiseSize;
    noiseCanvas.height = noiseSize;
    const nCtx = noiseCanvas.getContext('2d');
    const nImgData = nCtx.createImageData(noiseSize, noiseSize);
    
    for (let i = 0; i < nImgData.data.length; i += 4) {
        const val = Math.random() * 255;
        nImgData.data[i] = val;     // R
        nImgData.data[i + 1] = val; // G
        nImgData.data[i + 2] = val; // B
        nImgData.data[i + 3] = 255 * (0.05 + 0.1 * params.intensity); // A
    }
    nCtx.putImageData(nImgData, 0, 0);
    
    ctx.globalCompositeOperation = "overlay";
    ctx.fillStyle = ctx.createPattern(noiseCanvas, "repeat");
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    ctx.globalCompositeOperation = "source-over"; // Reset composite operation

    // 3. Draw "Souls" (Ethereal glowing orbs floating in the image)
    ctx.globalCompositeOperation = "screen";
    for (let i = 0; i < params.soulCount; i++) {
        // Randomize positions leaning slightly towards the center or bottom
        const x = Math.random() * canvas.width;
        const y = Math.random() * canvas.height;
        // Radius scales with image width
        const r = (Math.random() * 0.1 + 0.05) * canvas.width;
        
        const soulGrad = ctx.createRadialGradient(x, y, 0, x, y, r);
        soulGrad.addColorStop(0, "rgba(100, 220, 255, 0.7)"); // Bright cyan core
        soulGrad.addColorStop(0.2, "rgba(60, 150, 200, 0.4)"); 
        soulGrad.addColorStop(0.6, "rgba(20, 80, 120, 0.1)");
        soulGrad.addColorStop(1, "rgba(0, 0, 0, 0)");         // Fade to transparency
        
        ctx.fillStyle = soulGrad;
        ctx.beginPath();
        ctx.arc(x, y, r, 0, Math.PI * 2);
        ctx.fill();
    }
    ctx.globalCompositeOperation = "source-over"; // Reset composite operation

    // 4. Draw Case File Stamp
    if (params.stampText) {
        ctx.save();
        
        // Calculate font size responsive to image dimensions
        const stampSize = Math.max(16, canvas.width * 0.04);
        ctx.font = `bold ${stampSize}px "Courier New", Courier, monospace`;
        ctx.textAlign = "center";
        ctx.textBaseline = "middle";
        
        // Position at the bottom right/center ish
        const px = canvas.width * 0.7;
        const py = canvas.height * 0.85;
        ctx.translate(px, py);
        
        // Apply slight random rotation for authenticity of a physical stamp
        const rotation = -0.05 - (Math.random() * 0.08);
        ctx.rotate(rotation);
        
        const lines = params.stampText.split('-'); // Allow splitting into multiple lines with hyphen
        const padding = stampSize * 0.6;
        
        // Find widest text line
        let maxTextWidth = 0;
        lines.forEach(line => {
            const w = ctx.measureText(line.trim()).width;
            if (w > maxTextWidth) maxTextWidth = w;
        });

        const boxWidth = maxTextWidth + padding * 2;
        const boxHeight = (lines.length * stampSize) + padding * 2;
        
        // Stamp style (faded/distressed bloody red color)
        ctx.strokeStyle = "rgba(180, 30, 30, 0.85)";
        ctx.fillStyle = "rgba(180, 30, 30, 0.85)";
        ctx.lineWidth = Math.max(3, stampSize * 0.15);
        
        // Draw the border rectangle
        const startX = -boxWidth / 2;
        const startY = -boxHeight / 2;
        ctx.strokeRect(startX, startY, boxWidth, boxHeight);
        
        // Double-border effect for classic stamp
        const innerPadding = ctx.lineWidth * 2;
        ctx.lineWidth = Math.max(1, stampSize * 0.05);
        ctx.strokeRect(startX + innerPadding, startY + innerPadding, boxWidth - innerPadding * 2, boxHeight - innerPadding * 2);

        // Fill text lines
        lines.forEach((line, index) => {
            const lineY = startY + padding + (stampSize / 2) + (index * stampSize);
            ctx.fillText(line.trim(), 0, lineY);
        });
        
        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 Soul Collector Case Image Viewer is a stylistic image processing tool designed to transform standard photos into eerie, noir-style or supernatural case files. It applies atmospheric effects such as dark vignettes, film grain, and color themes like sepia or high-contrast grayscale. Users can also add ethereal glowing orbs and custom text stamps to create a distressed, investigative aesthetic. This tool is ideal for creators looking to enhance images for horror-themed projects, digital storytelling, tabletop gaming assets, or social media content with a mysterious atmosphere.

Leave a Reply

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