Please bookmark this page to avoid losing your image tool!

Image Da Vinci Notebook Page Creator

(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.
async function processImage(originalImg, sepiaIntensity = 1.0, textureAmount = 0.5, stainColor = 'rgba(100, 80, 50, 0.1)', annotationDensity = 3) {
    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');

    // Determine canvas size - add padding for notebook effect
    // Adjusted padding for better aesthetics and min value
    const padding = Math.max(25, Math.min(originalImg.width, originalImg.height) * 0.12);
    canvas.width = originalImg.width + 2 * padding;
    canvas.height = originalImg.height + 2 * padding;

    // --- 1. Parchment Background ---
    const basePaperColor = { r: 240, g: 230, b: 210 }; // A light linen/beige color
    ctx.fillStyle = `rgb(${basePaperColor.r}, ${basePaperColor.g}, ${basePaperColor.b})`;
    ctx.fillRect(0, 0, canvas.width, canvas.height);

    // Add noise for texture
    if (textureAmount > 0) {
        const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
        const pixels = imageData.data;
        // Max color variation; higher textureAmount gives more pronounced noise
        const noiseStrength = 40 * Math.max(0, Math.min(1, textureAmount)); 
        for (let i = 0; i < pixels.length; i += 4) {
            const randFactor = (Math.random() - 0.5) * noiseStrength;
            pixels[i] = Math.max(0, Math.min(255, pixels[i] + randFactor));     // Red
            pixels[i + 1] = Math.max(0, Math.min(255, pixels[i + 1] + randFactor)); // Green
            pixels[i + 2] = Math.max(0, Math.min(255, pixels[i + 2] + randFactor)); // Blue
        }
        ctx.putImageData(imageData, 0, 0);
    }
    
    // Add "stains" to the paper
    if (stainColor && stainColor.toLowerCase() !== 'none' && stainColor !== '' && textureAmount > 0.05) {
        const numStains = Math.floor(Math.random() * 4 + 2); // 2 to 5 stains
        for (let i = 0; i < numStains; i++) {
            ctx.fillStyle = stainColor; // Uses the provided semi-transparent stain color
            const stainX = Math.random() * canvas.width;
            const stainY = Math.random() * canvas.height;
            // Elliptical stains with random sizes and rotation
            const stainRadiusX = (Math.random() * canvas.width / 7) + (canvas.width / 12);
            const stainRadiusY = (Math.random() * canvas.height / 9) + (canvas.height / 15);
            
            ctx.beginPath();
            ctx.ellipse(stainX, stainY, stainRadiusX, stainRadiusY, Math.random() * Math.PI * 2, 0, Math.PI * 2);
            ctx.fill();
        }
    }

    // --- 2. Process and Draw the Image ---
    // Use a temporary canvas for image manipulation (e.g., sepia)
    const tempCanvas = document.createElement('canvas');
    tempCanvas.width = originalImg.width;
    tempCanvas.height = originalImg.height;
    const tempCtx = tempCanvas.getContext('2d');
    tempCtx.drawImage(originalImg, 0, 0, originalImg.width, originalImg.height);

    // Apply sepia filter if intensity is greater than 0
    if (sepiaIntensity > 0) {
        const imgData = tempCtx.getImageData(0, 0, tempCanvas.width, tempCanvas.height);
        const data = imgData.data;
        const si = Math.max(0, Math.min(1, sepiaIntensity)); // Clamp intensity [0, 1]

        for (let i = 0; i < data.length; i += 4) {
            const r = data[i];
            const g = data[i + 1];
            const b = data[i + 2];

            // Standard sepia calculation
            const sr = (r * 0.393) + (g * 0.769) + (b * 0.189);
            const sg = (r * 0.349) + (g * 0.686) + (b * 0.168);
            const sb = (r * 0.272) + (g * 0.534) + (b * 0.131);

            // Mix original with sepia based on intensity
            data[i] = Math.min(255, (sr * si) + (r * (1 - si)));
            data[i + 1] = Math.min(255, (sg * si) + (g * (1 - si)));
            data[i + 2] = Math.min(255, (sb * si) + (b * (1 - si)));
        }
        tempCtx.putImageData(imgData, 0, 0);
    }
    
    // Draw the processed image onto the main canvas with slight transparency to blend
    ctx.globalAlpha = 0.92; 
    ctx.drawImage(tempCanvas, padding, padding, originalImg.width, originalImg.height);
    ctx.globalAlpha = 1.0; // Reset global alpha
    
    // Add a very subtle border around the "pasted" image
    ctx.strokeStyle = 'rgba(0,0,0,0.08)';
    ctx.lineWidth = 1;
    ctx.strokeRect(padding - 0.5, padding - 0.5, originalImg.width + 1, originalImg.height + 1);

    // --- 3. Annotations (Mirrored Text and Diagram Lines) ---
    if (annotationDensity > 0) {
        const inkColorBase = { r: 70, g: 50, b: 30 }; // Darker, more sepia-like ink
        const inkOpacity = 0.45 + Math.min(0.35, sepiaIntensity * 0.4);
        const inkColor = `rgba(${inkColorBase.r}, ${inkColorBase.g}, ${inkColorBase.b}, ${inkOpacity})`;
        
        const fontSize = Math.max(10, Math.floor(padding / 4.8)); // Slightly smaller, more delicate font
        const lineHeight = fontSize * 1.2;

        // Helper function to add mirrored text blocks
        const addMirroredTextLines = (xAnchor, yAnchor, baseNumLines, lineLengthMaxPercentage, mirrored = true) => {
            const numLines = Math.max(1, Math.min(7, Math.floor(baseNumLines * (annotationDensity / 3))));
            if (numLines === 0 || padding < 20) return; // Min padding for text

            ctx.save();
            ctx.fillStyle = inkColor;
            ctx.font = `${fontSize}px 'Georgia', serif`; // Georgia for a classic feel
            
            const charSet = "abcdefghijklmnopqrstuvwxyz"; // Da Vinci's notes used lowercase
            const avgCharWidth = ctx.measureText("m").width; // Estimate average char width
            const lineLengthMax = canvas.width * lineLengthMaxPercentage;

            if (mirrored) {
                ctx.translate(xAnchor, yAnchor); // Move origin to the text's logical right anchor
                ctx.scale(-1, 1);              // Mirror context horizontally
                ctx.textAlign = 'right';       // Text will be right-aligned relative to new (0,0)
            } else {
                ctx.translate(xAnchor, yAnchor); // Move origin to the text's logical left anchor
                ctx.textAlign = 'left';
            }

            for (let i = 0; i < numLines; i++) {
                const currentLineYRelative = i * lineHeight;
                let text = "";
                // Vary line length for realism
                const numCharsInLine = Math.floor(((Math.random() * 0.4) + 0.6) * (lineLengthMax / avgCharWidth));
                
                for(let k=0; k < numCharsInLine; k++) {
                    text += charSet.charAt(Math.floor(Math.random() * charSet.length));
                    // Add spaces randomly
                    if (k > 0 && k < numCharsInLine -1 && Math.random() < 0.18 && text.slice(-1) !== ' ') {
                        text += " "; 
                    }
                }
                if (text.endsWith(" ")) text = text.slice(0, -1); // Trim trailing space
                ctx.fillText(text, 0, currentLineYRelative); // Draw relative to transformed origin
            }
            ctx.restore();
        };
        
        // Define text annotation locations
        if (padding >= 20) {
             // Top-left quadrant related annotation
            addMirroredTextLines(
                padding + originalImg.width * 0.03 + (canvas.width * 0.18), // xAnchor (right edge of text)
                padding * 0.75, // yAnchor (top of text block)
                2, // baseNumLines: scaled by annotationDensity
                0.18, // lineLengthMaxPercentage of canvas.width
                true
            );
            
            // Bottom-right quadrant related annotation
            const brNumLines = Math.max(1, Math.min(7, Math.floor(3 * (annotationDensity / 3))));
            const brTextBlockHeight = brNumLines * lineHeight;
            addMirroredTextLines(
                canvas.width - padding * 0.65, // xAnchor (right edge of text)
                canvas.height - padding * 0.75 - brTextBlockHeight, // yAnchor (top of text block)
                3, 
                0.22, // lineLengthMaxPercentage
                true
            );
        }

        // Add "diagrammatic" lines
        const numDiagramLines = Math.floor(Math.random() * annotationDensity * 1.8 + annotationDensity * 0.6);
        ctx.strokeStyle = inkColor;
        ctx.lineWidth = Math.max(0.7, fontSize / 15); // Thin lines for diagrams
        for (let i = 0; i < numDiagramLines; i++) {
            ctx.beginPath();
            let startX, startY;
            // Start lines from various places: image edges or margins
            if (Math.random() < 0.65) { // Start near/on image
                 startX = padding + Math.random() * originalImg.width;
                 startY = padding + Math.random() * originalImg.height;
            } else { // Start from margins
                 startX = (Math.random() < 0.5 ? Math.random() * padding * 0.7 : canvas.width - Math.random() * padding * 0.7);
                 startY = (Math.random() < 0.5 ? Math.random() * padding * 0.7 : canvas.height - Math.random() * padding * 0.7);
            }

            // End points with some randomness to create varied lines
            const endX = startX + (Math.random() - 0.5) * (originalImg.width / 2.2);
            const endY = startY + (Math.random() - 0.5) * (originalImg.height / 2.2);
            
            ctx.moveTo(startX, startY);
            // Use bezier curves for more organic-looking lines
            ctx.bezierCurveTo(
                startX + (Math.random() - 0.5) * 60, startY + (Math.random() - 0.5) * 60,
                endX + (Math.random() - 0.5) * 60, endY + (Math.random() - 0.5) * 60,
                endX, endY
            );

            // Add a small, simple arrowhead to some lines
            if (Math.random() < 0.35) {
                // Approximate angle at the end of the curve for arrowhead direction
                const lastControlX = endX + (Math.random() - 0.5) * 60; // (This is not perfect for Bezier end angle)
                const lastControlY = endY + (Math.random() - 0.5) * 60; // One of the control points for the curve
                // A simplified angle calculation
                const angle = Math.atan2(endY - lastControlY, endX - lastControlX); 
                const arrowSize = Math.max(3.5, fontSize / 3);
                ctx.moveTo(endX, endY); // Ensure path is at endX, endY before drawing arrow parts
                ctx.lineTo(endX - arrowSize * Math.cos(angle - Math.PI / 8), endY - arrowSize * Math.sin(angle - Math.PI / 8));
                ctx.moveTo(endX, endY);
                ctx.lineTo(endX - arrowSize * Math.cos(angle + Math.PI / 8), endY - arrowSize * Math.sin(angle + Math.PI / 8));
            }
            ctx.stroke();
        }
    }
    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 Da Vinci Notebook Page Creator is a versatile online tool that transforms your images into artistic notebook-style renderings. It adds a textured parchment background with customizable features such as sepia toning and stain effects to create a vintage, aged appearance. Additionally, the tool integrates decorative annotations, including mirrored text and diagrammatic lines that evoke the style of Da Vinci’s sketches. Ideal for artists, educators, and creative professionals, this tool can be used to create visually appealing presentations, unique artwork, or enhance personal projects with a classic touch.

Leave a Reply

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

Other Image Tools:

Image Dystopian Citizen ID Creator

Image Monster Hunter Bestiary Creator

Image Vintage Carnival Sideshow Poster Creator

Image Space Explorer’s Log Creator

Image Neolithic Petroglyph Frame Creator

Image Ukiyo-e Japanese Woodblock Print Creator

Image Persian Miniature Painting Creator

Image Sci-Fi Movie Poster Template Creator

Image Horror Movie Poster Template

Image Social Media Milestone Certificate Creator

Halloween Death Certificate Template

Image Anatomical Illustration Frame Creator

Image Romance Novel Cover Template Creator

Image Tabloid Headline Template

Image Space Mission Patch Template Creator

Image Cassette Tape Cover Template Creator

Image Passport Page Template Generator

Image Old Map Frame With Compass Rose Decorator

Image Diploma and Degree Certificate Framer

Image Soviet Propaganda Poster Style Generator

Image Yu-Gi-Oh Card Template Creator

Image Ancient Roman Greek Tablet Frame Creator

Image Marriage Certificate Template Creator

Image Video Game Achievement Frame Creator

Image Newspaper Front Page Template Creator

Image Botanical Illustration Frame Creator

Image Vinyl Record Sleeve Template Creator

Vintage Photo Booth Strip Template Generator

Image Cyberpunk Interface Frame Designer

Image Detective Novel Cover Template

Image Achievement Certificate Framer

Image Illuminated Manuscript Frame Generator

Image Art Deco Poster Frame Creator

Image Egyptian Papyrus Scroll Frame Designer

Image Vintage Postage Stamp Frame Creator

Image Magic: The Gathering Card Frame Generator

See All →