Please bookmark this page to avoid losing your image tool!

Image To KGB Secret Dossier Style Document Generator

(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 = "47-X / 1965",
    objectName = "НЕОПОЗНАННЫЙ ЛЕТАЮЩИЙ АППАРАТ",
    date = "01.08.2026",
    location = "Сектор 7",
    archiveStatus = "СЕКРЕТНО",
    bodyText = "О ПОХИЩЕННЫХ ЛЕТУЧИХ МЫШАХ\n\nОбстоятельства инцидента:\nПод покровом ночи на специализированный полигон проник неизвестный, было похищено как минимум 17 редких особей и ядовитая летучая мышь. На месте преступления найдены лишь обрывки сети, надкушенный манго и странный высокочастотный излучатель.\n\nПодозреваемые лица и улики: К делу приобщен неизвестный фигурант, чей лик зафиксирован на фотокарточке подозреваемого: неестественная челюсть и подозрительные действия в последние недели.\n\nЗаключение следствия:\nВиновный подлежит немедленной изоляции и допросу с применением спецсредств.\n\nПРИЛОЖЕНИЕ: ФОТОФИКСАЦИЯ АНОМАЛИИ",
    stampText = "СОВЕРШЕННО СЕКРЕТНО"
) {
    const canvas = document.createElement('canvas');
    const width = 850;
    const height = 1200;
    canvas.width = width;
    canvas.height = height;
    const ctx = canvas.getContext('2d');

    // 1. Generate Aged Paper Background
    ctx.fillStyle = '#ebd6b7';
    ctx.fillRect(0, 0, width, height);

    // Apply Noise and Grain
    const imgData = ctx.getImageData(0, 0, width, height);
    for (let i = 0; i < imgData.data.length; i += 4) {
        const noise = (Math.random() - 0.5) * 25;
        imgData.data[i] = Math.max(0, Math.min(255, imgData.data[i] + noise));     // R
        imgData.data[i+1] = Math.max(0, Math.min(255, imgData.data[i+1] + noise)); // G
        imgData.data[i+2] = Math.max(0, Math.min(255, imgData.data[i+2] + noise)); // B
    }
    ctx.putImageData(imgData, 0, 0);

    // Vignette Effect (Darken edges)
    const vignette = ctx.createRadialGradient(width/2, height/2, 200, width/2, height/2, height * 0.8);
    vignette.addColorStop(0, 'rgba(0,0,0,0)');
    vignette.addColorStop(1, 'rgba(60, 40, 20, 0.45)');
    ctx.fillStyle = vignette;
    ctx.fillRect(0, 0, width, height);

    // 2. Setup Typography & Top Header
    ctx.fillStyle = '#222';
    ctx.textAlign = 'center';
    
    // Top headers
    ctx.font = "bold 32px 'Courier New', Courier, monospace";
    ctx.fillText("КГБ СССР", width / 2, 70);
    ctx.font = "bold 26px 'Courier New', Courier, monospace";
    ctx.fillText("МАТЕРИАЛЫ ДЕЛА", width / 2, 105);
    
    // Line separator
    ctx.beginPath();
    ctx.moveTo(80, 125);
    ctx.lineTo(width - 80, 125);
    ctx.lineWidth = 2;
    ctx.strokeStyle = '#333';
    ctx.stroke();

    // 3. Info Fields (Left Side)
    ctx.textAlign = 'left';
    ctx.font = "bold 20px 'Courier New', Courier, monospace";
    
    const infoLines = [
        `ДЕЛО №: ${caseNumber}`,
        `ОБЪЕКТ: ${objectName}`,
        `ДАТА КОНТАКТА: ${date}`,
        `МЕСТО: ${location}`,
        `АРХИВ: ${archiveStatus}`
    ];

    let currentY = 180;
    for (const line of infoLines) {
        ctx.fillText(line, 80, currentY);
        currentY += 45;
    }

    // 4. Attach Photo (Right Side)
    const photoMaxWidth = 300;
    const photoMaxHeight = 300;
    const imgAspect = originalImg.width / originalImg.height;
    
    let pWidth = photoMaxWidth;
    let pHeight = pWidth / imgAspect;
    if (pHeight > photoMaxHeight) {
        pHeight = photoMaxHeight;
        pWidth = pHeight * imgAspect;
    }

    const pX = width - 80 - pWidth;
    const pY = 160;

    // Draw photo border
    ctx.save();
    ctx.translate(pX + pWidth/2, pY + pHeight/2);
    ctx.rotate(Math.random() * 0.06 - 0.03); // Slight random tilt
    ctx.translate(-(pX + pWidth/2), -(pY + pHeight/2));

    // White photo paper frame
    ctx.fillStyle = '#f4f4f4';
    ctx.shadowColor = 'rgba(0,0,0,0.5)';
    ctx.shadowBlur = 8;
    ctx.shadowOffsetX = 3;
    ctx.shadowOffsetY = 3;
    const border = 12;
    ctx.fillRect(pX - border, pY - border, pWidth + border * 2, pHeight + border * 2);
    
    // Remove shadow for the image itself
    ctx.shadowColor = 'transparent';
    
    // Apply KGB gritty B&W filter
    ctx.filter = 'grayscale(100%) contrast(160%) sepia(20%) brightness(90%)';
    ctx.drawImage(originalImg, pX, pY, pWidth, pHeight);
    ctx.filter = 'none';

    // Draw Tape (Top Left, Bottom Right of the photo frame)
    function drawTape(x, y, angle) {
        ctx.save();
        ctx.translate(x, y);
        ctx.rotate(angle);
        ctx.fillStyle = 'rgba(230, 220, 180, 0.6)';
        ctx.shadowColor = 'rgba(0,0,0,0.2)';
        ctx.shadowBlur = 3;
        ctx.fillRect(-30, -10, 60, 20);
        ctx.restore();
    }
    drawTape(pX - border, pY - border, -Math.PI / 4);
    drawTape(pX + pWidth + border, pY + pHeight + border, -Math.PI / 4);
    
    ctx.restore();

    // 5. Body Text (Typewriter Style)
    ctx.font = "20px 'Courier New', Courier, monospace";
    ctx.fillStyle = '#262626'; // Not pitch black, slightly faded ink
    const contentYStart = Math.max(currentY + 20, pY + pHeight + 40);
    currentY = contentYStart;
    const textMaxWidth = width - 160;

    // Function to wrap and draw text
    function drawParagraph(text, x, y, maxWidth, lineHeight) {
        const paragraphs = text.split('\n');
        for (const para of paragraphs) {
            const words = para.split(' ');
            let line = '';
            for (let n = 0; n < words.length; n++) {
                const testLine = line + words[n] + ' ';
                const metrics = ctx.measureText(testLine);
                const testWidth = metrics.width;
                if (testWidth > maxWidth && n > 0) {
                    // Randomize alpha slightly for typewriter effect
                    ctx.globalAlpha = 0.8 + Math.random() * 0.2;
                    ctx.fillText(line, x, y);
                    line = words[n] + ' ';
                    y += lineHeight;
                } else {
                    line = testLine;
                }
            }
            if (line.trim().length > 0) {
                ctx.globalAlpha = 0.8 + Math.random() * 0.2;
                ctx.fillText(line, x, y);
                y += lineHeight;
            }
            // Paragraph spacing
            y += lineHeight * 0.5;
        }
        ctx.globalAlpha = 1.0;
        return y;
    }

    currentY = drawParagraph(bodyText, 80, currentY, textMaxWidth, 28);

    // Signatures Area
    currentY += 40;
    ctx.font = "bold 20px 'Courier New', Courier, monospace";
    ctx.fillText("Начальник следственного комитета: _________________", 80, currentY);
    
    // Fake hand-written squiggle
    ctx.save();
    ctx.strokeStyle = '#1b315e'; // Faded pen blue
    ctx.lineWidth = 2;
    ctx.beginPath();
    ctx.moveTo(500, currentY - 5);
    ctx.bezierCurveTo(520, currentY - 20, 540, currentY + 10, 550, currentY - 5);
    ctx.bezierCurveTo(570, currentY - 15, 590, currentY, 610, currentY - 8);
    ctx.bezierCurveTo(620, currentY - 10, 630, currentY - 2, 640, currentY - 12);
    ctx.stroke();
    ctx.restore();

    // 6. Stamps and Overlays (Multiply blend mode)
    ctx.globalCompositeOperation = 'multiply';

    // Rectangular Top Secret Stamp
    ctx.save();
    ctx.translate(width / 2 + 100, contentYStart - 40);
    ctx.rotate(-15 * Math.PI / 180);
    ctx.strokeStyle = '#b01111';
    ctx.fillStyle = '#b01111';
    ctx.lineWidth = 4;
    ctx.font = "bold 42px 'Times New Roman', Times, serif";
    
    // Add grit/stamp effect by making alpha 0.7
    ctx.globalAlpha = 0.7;
    
    const stampWidth = ctx.measureText(stampText).width + 30;
    const stampHeight = 60;
    
    // Outer box
    ctx.strokeRect(-stampWidth/2, -stampHeight/2, stampWidth, stampHeight);
    // Inner box
    ctx.lineWidth = 1.5;
    ctx.strokeRect(-stampWidth/2 + 6, -stampHeight/2 + 6, stampWidth - 12, stampHeight - 12);
    
    ctx.textAlign = 'center';
    ctx.textBaseline = 'middle';
    ctx.fillText(stampText, 0, 2);
    ctx.restore();

    // Circular Archive Stamp
    ctx.save();
    ctx.translate(220, currentY - 30);
    ctx.rotate(Math.random() * Math.PI); // Random rotation
    ctx.globalAlpha = 0.6;
    ctx.strokeStyle = '#432d66'; // Purple-ish ink
    ctx.fillStyle = '#432d66';
    ctx.lineWidth = 3;
    
    // Outer Circle
    ctx.beginPath();
    ctx.arc(0, 0, 60, 0, Math.PI * 2);
    ctx.stroke();
    // Inner Circle
    ctx.beginPath();
    ctx.arc(0, 0, 50, 0, Math.PI * 2);
    ctx.stroke();
    
    ctx.textAlign = 'center';
    ctx.textBaseline = 'middle';
    ctx.font = "bold 16px Arial, sans-serif";
    ctx.fillText("КГБ СССР", 0, -15);
    ctx.fillText("АРХИВ", 0, 5);
    ctx.font = "12px Arial, sans-serif";
    ctx.fillText("★ СЕКРЕТНО ★", 0, 25);
    ctx.restore();

    ctx.globalCompositeOperation = 'source-over';

    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

This tool transforms your uploaded images into stylized, vintage-looking secret dossier documents. It applies a variety of aesthetic effects including aged paper textures, grainy noise, vignette shading, and a gritty black-and-white filter to your photo to make it look like a historical surveillance image. Users can customize details such as case numbers, object names, dates, locations, and archive statuses, and add custom body text. The generator also includes thematic elements like typewriter-style typography, faux handwritten signatures, and ‘Top Secret’ style stamps. This tool is ideal for creative projects, role-playing games, themed parties, or social media content looking for a dramatic, espionage-inspired aesthetic.

Leave a Reply

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