Please bookmark this page to avoid losing your image tool!

Old Books Photo 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, title = "A Journey Through Time", subtitle = "Chapter I") {
    // Determine canvas dimensions
    const width = 800;
    const height = 600;
    
    // Create canvas
    const canvas = document.createElement('canvas');
    canvas.width = width;
    canvas.height = height;
    const ctx = canvas.getContext('2d');

    // 1. Draw Wooden Desk Background
    ctx.fillStyle = '#1e110a';
    ctx.fillRect(0, 0, width, height);

    // Add faux wood grain stripes
    ctx.lineWidth = 4;
    for(let i = 0; i < 200; i++) {
        ctx.strokeStyle = `rgba(0, 0, 0, ${Math.random() * 0.4})`;
        let x = Math.random() * width;
        ctx.beginPath();
        ctx.moveTo(x, 0);
        ctx.lineTo(x + (Math.random() * 10 - 5), height);
        ctx.stroke();
    }
    
    // Smooth radial lighting over the wood
    let deskLighting = ctx.createRadialGradient(width/2, height/2, 100, width/2, height/2, 600);
    deskLighting.addColorStop(0, 'rgba(255,255,255,0.05)');
    deskLighting.addColorStop(1, 'rgba(0,0,0,0.85)');
    ctx.fillStyle = deskLighting;
    ctx.fillRect(0, 0, width, height);

    // 2. Draw Book Cover Outline (Dark Leather)
    ctx.save();
    ctx.shadowColor = 'rgba(0, 0, 0, 0.8)';
    ctx.shadowBlur = 20;
    ctx.shadowOffsetX = 5;
    ctx.shadowOffsetY = 10;
    ctx.fillStyle = '#2b160e'; // Very dark reddish brown
    
    // Draw rounded rect manually for broad compatibility
    ctx.beginPath();
    ctx.moveTo(60, 50);
    ctx.lineTo(740, 50);
    ctx.quadraticCurveTo(760, 50, 760, 70);
    ctx.lineTo(760, 550);
    ctx.quadraticCurveTo(760, 570, 740, 570);
    ctx.lineTo(60, 570);
    ctx.quadraticCurveTo(40, 570, 40, 550);
    ctx.lineTo(40, 70);
    ctx.quadraticCurveTo(40, 50, 60, 50);
    ctx.closePath();
    ctx.fill();
    ctx.restore();

    // The protruding book spine (leather bump)
    ctx.fillStyle = '#110804';
    ctx.beginPath();
    ctx.moveTo(375, 45);
    ctx.quadraticCurveTo(400, 55, 425, 45);
    ctx.lineTo(425, 575);
    ctx.quadraticCurveTo(400, 565, 375, 575);
    ctx.closePath();
    ctx.fill();

    // 3. Draw Book Pages (Stacking them up to create 3D thickness)
    for (let i = 12; i >= 0; i--) {
        if (i === 12) { // Add drop shadow to bottom-most pages
            ctx.shadowColor = 'rgba(0,0,0,0.5)';
            ctx.shadowBlur = 10;
            ctx.shadowOffsetX = 3;
            ctx.shadowOffsetY = 3;
        } else {
            ctx.shadowColor = 'transparent';
        }
        
        ctx.beginPath();
        // Left Page geometry
        ctx.moveTo(70 - i, 80 + i * 0.5);
        ctx.quadraticCurveTo(235 - i * 0.5, 70 + i * 0.5, 400, 90 + i * 0.5); // Top edge
        ctx.lineTo(400, 510 + i * 0.5); // Center Spine
        ctx.quadraticCurveTo(235 - i * 0.5, 530 + i * 0.5, 70 - i, 520 + i * 0.5); // Bottom edge
        ctx.closePath();

        // Right Page geometry
        ctx.moveTo(400, 90 + i * 0.5);
        ctx.quadraticCurveTo(565 + i * 0.5, 70 + i * 0.5, 730 + i, 80 + i * 0.5); // Top edge
        ctx.lineTo(730 + i, 520 + i * 0.5); // Outer right edge
        ctx.quadraticCurveTo(565 + i * 0.5, 530 + i * 0.5, 400, 510 + i * 0.5); // Bottom edge
        ctx.closePath();
        
        ctx.fillStyle = (i % 2 === 0) ? '#d9d1b0' : '#e6deb8'; // Alternating shadow color for pages
        if (i === 0) {
            // Fill top-most page with realistic spine-shadow gradient
            
            // Left page overlay
            let gradLeft = ctx.createLinearGradient(70, 0, 400, 0);
            gradLeft.addColorStop(0, '#e8e0c0'); 
            gradLeft.addColorStop(0.8, '#f5efd3'); 
            gradLeft.addColorStop(1, '#a89f83'); 
            
            ctx.fillStyle = gradLeft;
            ctx.beginPath();
            ctx.moveTo(70, 80);
            ctx.quadraticCurveTo(235, 70, 400, 90);
            ctx.lineTo(400, 510);
            ctx.quadraticCurveTo(235, 530, 70, 520);
            ctx.closePath();
            ctx.fill();

            // Right page overlay
            let gradRight = ctx.createLinearGradient(400, 0, 730, 0);
            gradRight.addColorStop(0, '#a89f83'); 
            gradRight.addColorStop(0.2, '#f5efd3'); 
            gradRight.addColorStop(1, '#e8e0c0');
            
            ctx.fillStyle = gradRight;
            ctx.beginPath();
            ctx.moveTo(400, 90);
            ctx.quadraticCurveTo(565, 70, 730, 80);
            ctx.lineTo(730, 520);
            ctx.quadraticCurveTo(565, 530, 400, 510);
            ctx.closePath();
            ctx.fill();
        } else {
            ctx.fill();
        }
    }
    
    // Center spine dark shadow fold
    let spineShadow = ctx.createLinearGradient(380, 0, 420, 0);
    spineShadow.addColorStop(0, 'rgba(0,0,0,0)');
    spineShadow.addColorStop(0.3, 'rgba(0,0,0,0.1)');
    spineShadow.addColorStop(0.5, 'rgba(0,0,0,0.4)'); // Deep fold
    spineShadow.addColorStop(0.7, 'rgba(0,0,0,0.1)');
    spineShadow.addColorStop(1, 'rgba(0,0,0,0)');
    ctx.fillStyle = spineShadow;
    ctx.fillRect(380, 80, 40, 440);

    // Define top page shape for clipping bounds
    const definePageBounds = () => {
        ctx.beginPath();
        // Left outline
        ctx.moveTo(70, 80);
        ctx.quadraticCurveTo(235, 70, 400, 90);
        ctx.lineTo(400, 510);
        ctx.quadraticCurveTo(235, 530, 70, 520);
        // Right outline
        ctx.lineTo(400, 510);
        ctx.quadraticCurveTo(565, 530, 730, 520);
        ctx.lineTo(730, 80);
        ctx.quadraticCurveTo(565, 70, 400, 90);
        ctx.closePath();
    };

    // 4. Add "Old Paper" texture (dirt / spots)
    ctx.save();
    definePageBounds();
    ctx.clip(); // Ensure texture does not spill off pages
    
    for (let j = 0; j < 300; j++) {
        let x = 70 + Math.random() * 660;
        let y = 70 + Math.random() * 460;
        ctx.fillStyle = `rgba(100, 70, 40, ${Math.random() * 0.08})`;
        ctx.beginPath();
        ctx.arc(x, y, Math.random() * 3, 0, Math.PI * 2);
        ctx.fill();
    }
    ctx.restore();

    // 5. Draw Faded Antique Text on Left Page
    ctx.save();
    ctx.fillStyle = 'rgba(60, 40, 25, 0.7)'; // Faded sepia ink
    ctx.textAlign = 'center';
    
    // Headers
    ctx.font = 'italic 26px "Georgia", "Times New Roman", serif';
    ctx.fillText(subtitle, 235, 120);
    ctx.font = '18px "Georgia", "Times New Roman", serif';
    ctx.fillText(title, 235, 150);

    // Decorative line
    ctx.strokeStyle = 'rgba(60, 40, 25, 0.4)';
    ctx.lineWidth = 1;
    ctx.beginPath();
    ctx.moveTo(150, 170); 
    ctx.lineTo(320, 170);
    ctx.stroke();

    // Left page lore text
    ctx.textAlign = 'left';
    ctx.font = '14px "Georgia", "Times New Roman", serif';
    let text = "There are memories that time simply cannot erase. Woven into the worn fibers of this bound artifact, the moments captured within tell a poignant story of origins, of paths once wandered, and lives lived to their absolute fullest.\n\nLike echoes from yesteryear, these faces gaze outward through the veil of time, frozen securely in sepia and faded ink. We watch as the days fade in tandem with the setting sun, but rest assured, the impression left upon the paper and soul remains everlasting...\n\nBehold the visage of past eras, serving forever as a timeless testament to a fleeting, yet profoundly beautiful existence.";
    
    let textY = 210;
    let paragraphs = text.split('\n');
    paragraphs.forEach(p => {
        let words = p.split(' ');
        let line = '';
        for (let k = 0; k < words.length; k++) {
            let testLine = line + words[k] + ' ';
            let metrics = ctx.measureText(testLine);
            if (metrics.width > 220 && k > 0) {
                ctx.fillText(line, 125, textY);
                line = words[k] + ' ';
                textY += 21;
            } else {
                line = testLine;
            }
        }
        ctx.fillText(line, 125, textY);
        textY += 22; // Paragraph bottom spacing
    });
    ctx.restore();

    // 6. Center and Vintage-Filter Original Image onto Right Page
    let boxW = 220;
    let boxH = 320;
    let scaleX = boxW / originalImg.width;
    let scaleY = boxH / originalImg.height;
    let scale = Math.min(scaleX, scaleY);
    let dw = originalImg.width * scale;
    let dh = originalImg.height * scale;
    
    // Right page center
    let dx = 565; 
    let dy = 300; 

    ctx.save();
    ctx.translate(dx, dy);
    // Add slight playful rotation for scrap-book feel
    ctx.rotate((Math.PI / 180) * 1.5); 
    
    // Drop shadow behind pasted photo
    ctx.shadowColor = 'rgba(0,0,0,0.5)';
    ctx.shadowBlur = 5;
    ctx.shadowOffsetX = 2;
    ctx.shadowOffsetY = 2;

    // Apply old photo filters (Sepia, Lower Brightness, Higher Contrast)
    ctx.filter = 'sepia(0.8) contrast(1.15) brightness(0.9)';
    ctx.drawImage(originalImg, -dw/2, -dh/2, dw, dh);
    ctx.filter = 'none'; // reset filter
    
    // Vintage thick white photo border
    ctx.strokeStyle = 'rgba(255, 250, 240, 0.9)';
    ctx.lineWidth = 6;
    ctx.strokeRect(-dw/2, -dh/2, dw, dh);
    ctx.shadowColor = 'transparent'; // reset shadow

    // Little scrap-book black photograph corner-holders
    ctx.fillStyle = '#1a1a1a';
    let c = 15; // corner bracket size
    
    // Top-Left corner
    ctx.beginPath(); 
    ctx.moveTo(-dw/2, -dh/2); 
    ctx.lineTo(-dw/2 + c, -dh/2); 
    ctx.lineTo(-dw/2, -dh/2 + c); 
    ctx.fill();
    // Top-Right corner
    ctx.beginPath(); 
    ctx.moveTo(dw/2, -dh/2); 
    ctx.lineTo(dw/2 - c, -dh/2); 
    ctx.lineTo(dw/2, -dh/2 + c); 
    ctx.fill();
    // Bottom-Left corner
    ctx.beginPath(); 
    ctx.moveTo(-dw/2, dh/2); 
    ctx.lineTo(-dw/2 + c, dh/2); 
    ctx.lineTo(-dw/2, dh/2 - c); 
    ctx.fill();
    // Bottom-Right corner
    ctx.beginPath(); 
    ctx.moveTo(dw/2, dh/2); 
    ctx.lineTo(dw/2 - c, dh/2); 
    ctx.lineTo(dw/2, dh/2 - c); 
    ctx.fill();
    ctx.restore();

    // 7. General atmospheric lighting/sepia tint over the entire canvas 
    ctx.fillStyle = 'rgba(120, 80, 40, 0.08)'; // Yellowish/brown warm hue
    ctx.fillRect(0, 0, width, height);

    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 Old Books Photo Viewer transforms your images into a vintage, scrapbook-style visual. It places your uploaded photo onto the right-hand page of a realistic, 3D-rendered antique leather-bound book, complete with aged paper textures, a dark wooden desk background, and a sepia-toned photographic filter. The tool also adds decorative elements like scrapbook corner holders and elegant, faded antique text on the left page to enhance the storytelling aspect. This tool is ideal for creating nostalgic social media posts, digital scrapbooks, or thematic presentation elements for historical or sentimental projects.

Leave a Reply

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