Please bookmark this page to avoid losing your image tool!

Image To Book Converter

(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, coverColor = "#3a2318", bgColor = "#e4d9c5", curveFactor = 0.05, thicknessFactor = 0.04) {
    // Parse parameters
    let factorC = parseFloat(curveFactor) || 0.05;
    let factorT = parseFloat(thicknessFactor) || 0.04;

    // Scale image reasonably to maintain performance and high quality
    let targetW = originalImg.width;
    if (targetW < 600) targetW = 600;
    if (targetW > 1200) targetW = 1200;
    let scale = targetW / originalImg.width;
    let w = Math.floor(originalImg.width * scale);
    let h = Math.floor(originalImg.height * scale);

    // Render image to a temp canvas for fast slice sampling
    let srcCanvas = document.createElement('canvas');
    srcCanvas.width = w;
    srcCanvas.height = h;
    let srcCtx = srcCanvas.getContext('2d', { willReadFrequently: true });
    srcCtx.drawImage(originalImg, 0, 0, w, h);

    // Book geometry parameters
    let curve = w * factorC;
    let thickness = w * factorT;
    let margin = w * 0.15; // padding for shadow and visuals

    let outW = w + margin * 2;
    let outH = h + curve * 4 + thickness + margin * 2;
    let baseY = margin + curve * 2; // initial Y position of the book edges

    let destCanvas = document.createElement('canvas');
    destCanvas.width = outW;
    destCanvas.height = outH;
    let ctx = destCanvas.getContext('2d');

    // 1. Draw flat background
    ctx.fillStyle = bgColor;
    ctx.fillRect(0, 0, outW, outH);

    ctx.lineJoin = 'round';
    ctx.lineCap = 'round';

    // Helper for book curvature offset (Z axis mapped to Y screen axis)
    // t goes from 0 (spine) to 1 (outer edge)
    function getCurveZ(t) {
        let foldD = curve * 1.5;  // depth of the center fold
        let peakD = curve * 1.2;  // height of the page curve peak
        // Smooth sine wave mapping simulating realistic page foreshortening and slump
        return -Math.sin(Math.pow(t, 0.6) * Math.PI) * peakD + (1 - t) * foldD;
    }

    let step = 8; // detail step for drawing thick block lines

    // 2. Draw shadow and book cover
    let cvPad = w * 0.012; // padding so the cover extends past the pages
    ctx.shadowColor = 'rgba(0, 0, 0, 0.4)';
    ctx.shadowBlur = w * 0.025;
    ctx.shadowOffsetX = w * 0.005;
    ctx.shadowOffsetY = w * 0.015;

    ctx.fillStyle = coverColor;
    ctx.beginPath();
    ctx.moveTo(margin - cvPad, baseY + getCurveZ(1) - cvPad);
    for (let x = 0; x <= w; x += step) {
        let t = x < w / 2 ? (w / 2 - x) / (w / 2) : (x - w / 2) / (w / 2);
        ctx.lineTo(margin + x, baseY + getCurveZ(t) - cvPad);
    }
    // Curve down the right edge
    ctx.lineTo(margin + w + cvPad, baseY + getCurveZ(1) - cvPad);
    ctx.lineTo(margin + w + cvPad, baseY + getCurveZ(1) + h + thickness + cvPad);
    // Trace back bottom curve
    for (let x = w; x >= 0; x -= step) {
        let t = x < w / 2 ? (w / 2 - x) / (w / 2) : (x - w / 2) / (w / 2);
        ctx.lineTo(margin + x, baseY + getCurveZ(t) + h + thickness + cvPad);
    }
    ctx.lineTo(margin - cvPad, baseY + getCurveZ(1) + h + thickness + cvPad);
    ctx.closePath();
    ctx.fill();

    // Disable shadow for remaining inner elements
    ctx.shadowColor = 'transparent';

    // 3. Draw bottom edge of the pages (thickness showing paper block)
    ctx.fillStyle = '#e5e5df';
    ctx.beginPath();
    ctx.moveTo(margin, baseY + getCurveZ(1) + h);
    for (let x = 0; x <= w; x += step) {
        let t = x < w / 2 ? (w / 2 - x) / (w / 2) : (x - w / 2) / (w / 2);
        ctx.lineTo(margin + x, baseY + getCurveZ(t) + h);
    }
    ctx.lineTo(margin + w, baseY + getCurveZ(1) + h + thickness);
    for (let x = w; x >= 0; x -= step) {
        let t = x < w / 2 ? (w / 2 - x) / (w / 2) : (x - w / 2) / (w / 2);
        ctx.lineTo(margin + x, baseY + getCurveZ(t) + h + thickness);
    }
    ctx.closePath();
    ctx.fill();

    // 4. Draw side depths (Left and Right page stacks)
    // Left side
    ctx.fillStyle = '#dcdcd4'; // darker due to light logic
    ctx.beginPath();
    ctx.moveTo(margin, baseY + getCurveZ(1));
    ctx.lineTo(margin, baseY + getCurveZ(1) + h);
    ctx.lineTo(margin, baseY + getCurveZ(1) + h + thickness);
    ctx.lineTo(margin, baseY + getCurveZ(1) + thickness);
    ctx.closePath();
    ctx.fill();

    // Right side
    ctx.fillStyle = '#f4f4ec'; // lighter
    ctx.beginPath();
    ctx.moveTo(margin + w, baseY + getCurveZ(1));
    ctx.lineTo(margin + w, baseY + getCurveZ(1) + h);
    ctx.lineTo(margin + w, baseY + getCurveZ(1) + h + thickness);
    ctx.lineTo(margin + w, baseY + getCurveZ(1) + thickness);
    ctx.closePath();
    ctx.fill();

    // 5. Draw realistic page stratification lines along the exposed thickness
    ctx.strokeStyle = 'rgba(0, 0, 0, 0.05)';
    ctx.lineWidth = 1;
    for (let dy = 2.5; dy < thickness; dy += 3.5) {
        // Bottom paper lines
        ctx.beginPath();
        for (let x = 0; x <= w; x += step) {
            let t = x < w / 2 ? (w / 2 - x) / (w / 2) : (x - w / 2) / (w / 2);
            let px = margin + x;
            let py = baseY + getCurveZ(t) + h + dy;
            if (x === 0) ctx.moveTo(px, py);
            else ctx.lineTo(px, py);
        }
        ctx.stroke();

        // Right side paper lines
        ctx.beginPath();
        ctx.moveTo(margin + w, baseY + getCurveZ(1) + dy);
        ctx.lineTo(margin + w, baseY + getCurveZ(1) + h + dy);
        ctx.stroke();

        // Left side paper lines
        ctx.beginPath();
        ctx.moveTo(margin, baseY + getCurveZ(1) + dy);
        ctx.lineTo(margin, baseY + getCurveZ(1) + h + dy);
        ctx.stroke();
    }

    // 6. Establish clipping region to draw perfectly antialiased curve bounds for pages
    ctx.save();
    ctx.beginPath();
    ctx.moveTo(margin, baseY + getCurveZ(1));
    for (let x = 0; x <= w; x += 1) { 
        let t = x < w / 2 ? (w / 2 - x) / (w / 2) : (x - w / 2) / (w / 2);
        ctx.lineTo(margin + x, baseY + getCurveZ(t));
    }
    ctx.lineTo(margin + w, baseY + getCurveZ(1) + h);
    for (let x = w; x >= 0; x -= 1) {
        let t = x < w / 2 ? (w / 2 - x) / (w / 2) : (x - w / 2) / (w / 2);
        ctx.lineTo(margin + x, baseY + getCurveZ(t) + h);
    }
    ctx.lineTo(margin, baseY + getCurveZ(1) + h);
    ctx.closePath();

    // Fill white paper color behind image first (fixes images with transparency)
    ctx.fillStyle = '#fdfcf7';
    ctx.fill();
    ctx.clip(); // Mask subsequent rendering

    // 7. Draw the image wrapping across the 3D curves via 1px column slicing
    for (let x = 0; x < w; x++) {
        let isLeft = x < w / 2;
        let t = isLeft ? (w / 2 - x) / (w / 2) : (x - w / 2) / (w / 2);
        let z = getCurveZ(t);
        
        let destX = margin + x;
        let destY = baseY + z;

        // Draw overlapping column slices (+0.2 width overlaps prevent seam gaps)
        ctx.drawImage(srcCanvas, x, 0, 1, h, destX, destY - 1, 1.2, h + 2);

        // Map surface slope to geometric lighting intensity
        let nextT = isLeft ? (w / 2 - (x + 1)) / (w / 2) : ((x + 1) - w / 2) / (w / 2);
        let nextZ = getCurveZ(nextT);
        let slope = nextZ - z;

        let alpha = 0;
        if (slope > 0) { // Tilt facing away from pseudo top surface
            alpha = Math.min(0.65, slope * 3.0);
            ctx.fillStyle = `rgba(0, 0, 0, ${alpha})`;
        } else { // Tilt facing towards theoretical light
            alpha = Math.min(0.55, -slope * 3.0);
            ctx.fillStyle = `rgba(255, 255, 255, ${alpha})`;
        }
        if (alpha > 0) ctx.fillRect(destX, destY - 1, 1.2, h + 2);

        // Extra dynamic shadows for physical fold realism
        if (t < 0.22) {
            let spineShadow = Math.min(0.65, (0.22 - t) * 3);
            ctx.fillStyle = `rgba(0, 0, 0, ${spineShadow})`;
            ctx.fillRect(destX, destY - 1, 1.2, h + 2);
        }
        if (t < 0.015) { // Sharp deep center gap
            ctx.fillStyle = `rgba(0, 0, 0, 0.45)`;
            ctx.fillRect(destX, destY - 1, 1.2, h + 2);
        }
        if (t > 0.92) { // Outer edge darkening to simulate page curvature
            let edgeShadow = (t - 0.92) * 5;
            ctx.fillStyle = `rgba(0, 0, 0, ${edgeShadow})`;
            ctx.fillRect(destX, destY - 1, 1.2, h + 2);
        }
    }
    
    ctx.restore(); // Drop clip bounds

    // 8. Visual details over the mapped pages
    let spineX = margin + w / 2;
    let spineTopY = baseY + getCurveZ(0);

    // Crisp center crease
    ctx.beginPath();
    ctx.moveTo(spineX, spineTopY);
    ctx.lineTo(spineX, spineTopY + h);
    ctx.strokeStyle = 'rgba(0,0,0,0.2)';
    ctx.lineWidth = 1;
    ctx.stroke();

    // Crease highlight
    ctx.beginPath();
    ctx.moveTo(spineX + 1, spineTopY);
    ctx.lineTo(spineX + 1, spineTopY + h);
    ctx.strokeStyle = 'rgba(255,255,255,0.15)';
    ctx.stroke();

    return destCanvas;
}

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 To Book Converter transforms standard images into realistic 3D-style book illustrations. Using advanced geometric mapping and lighting effects, the tool wraps your image across curved pages, simulates a physical spine crease, and adds details like page thickness, shadows, and paper textures. This is an ideal tool for creators looking to generate mockups for book covers, enhance digital art for social media, or create unique visual assets for presentations and storytelling projects.

Leave a Reply

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