Please bookmark this page to avoid losing your image tool!

Image New Bow Adder

(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.
/**
 * Processes an image by overlaying a procedurally generated "New Bow" (бант).
 * 
 * @param {HTMLImageElement} originalImg - The source image
 * @param {string} bowColor - The CSS color of the bow (default: '#d60036' - crimson red)
 * @param {number} bowSize - The size of the bow relative to the image width (default: 0.4)
 * @param {number} positionX - Horizontal position ratio from 0 to 1 (default: 0.5)
 * @param {number} positionY - Vertical position ratio from 0 to 1 (default: 0.2)
 * @param {number} angle - Rotation angle in degrees (default: 0)
 * @returns {HTMLCanvasElement} A canvas element containing the final merged image
 */
function processImage(originalImg, bowColor = "#d60036", bowSize = 0.4, positionX = 0.5, positionY = 0.2, angle = 0) {
    // 1. Sanitize Inputs
    bowSize = typeof bowSize === 'number' ? bowSize : parseFloat(bowSize);
    if (isNaN(bowSize) || bowSize <= 0) bowSize = 0.4;

    positionX = typeof positionX === 'number' ? positionX : parseFloat(positionX);
    if (isNaN(positionX)) positionX = 0.5;

    positionY = typeof positionY === 'number' ? positionY : parseFloat(positionY);
    if (isNaN(positionY)) positionY = 0.2;

    angle = typeof angle === 'number' ? angle : parseFloat(angle);
    if (isNaN(angle)) angle = 0;

    if (typeof bowColor !== 'string') bowColor = "#d60036";

    // 2. Setup Canvas
    const canvas = document.createElement('canvas');
    canvas.width = originalImg.width;
    canvas.height = originalImg.height;
    const ctx = canvas.getContext('2d');

    // Draw the original image as base
    ctx.drawImage(originalImg, 0, 0);

    // 3. Compute Bow Geometry
    let width = canvas.width * bowSize;
    let height = width * 0.7; // Aspect ratio of the bow parts
    let cx = canvas.width * positionX;
    let cy = canvas.height * positionY;

    // 4. Color Processing Utilities
    // Normalizes ANY valid CSS color string to base RGB to compute shading gradients
    function normalizeColor(colorStr) {
        const tmpCnv = document.createElement("canvas");
        tmpCnv.width = 1; 
        tmpCnv.height = 1;
        const tmpCtx = tmpCnv.getContext("2d");
        tmpCtx.fillStyle = colorStr;
        tmpCtx.fillRect(0, 0, 1, 1);
        const data = tmpCtx.getImageData(0, 0, 1, 1).data;
        return { r: data[0], g: data[1], b: data[2] };
    }

    function adjustColor(rgb, factor) {
        let r = Math.min(255, Math.max(0, Math.floor(rgb.r * factor)));
        let g = Math.min(255, Math.max(0, Math.floor(rgb.g * factor)));
        let b = Math.min(255, Math.max(0, Math.floor(rgb.b * factor)));
        return `rgb(${r},${g},${b})`;
    }

    let baseObj = normalizeColor(bowColor);
    // Prevent pure black from lacking any 3D shadow depth
    if (baseObj.r === 0 && baseObj.g === 0 && baseObj.b === 0) {
        baseObj = { r: 25, g: 25, b: 25 };
    }

    const colLight = adjustColor(baseObj, 1.6);
    const colBase = adjustColor(baseObj, 1.0);
    const colDark = adjustColor(baseObj, 0.5);
    const colStroke = adjustColor(baseObj, 0.3);

    // 5. Canvas Drawing Context Configuration
    ctx.save();
    ctx.translate(cx, cy);
    ctx.rotate(angle * Math.PI / 180);

    // Create 3D radial gradient for satin-like fabric
    const bowGrad = ctx.createRadialGradient(0, -height * 0.2, width * 0.05, 0, 0, width * 0.6);
    bowGrad.addColorStop(0, colLight);
    bowGrad.addColorStop(0.3, colBase);
    bowGrad.addColorStop(1, colDark);

    ctx.fillStyle = bowGrad;
    ctx.strokeStyle = colStroke;
    ctx.lineWidth = Math.max(1, width * 0.015);
    ctx.lineJoin = 'round';
    ctx.lineCap = 'round';
    
    // Add realistic outer drop shadow
    const shadowColorOverlay = "rgba(0, 0, 0, 0.4)";
    ctx.shadowColor = shadowColorOverlay;
    ctx.shadowBlur = Math.max(4, width * 0.04);
    ctx.shadowOffsetY = Math.max(2, width * 0.02);

    const w = width;
    const h = height;

    // 6. Draw the Components of the Bow procedurally
    // Draw Ribbons First (Back layer)
    function drawRibbon(isLeft) {
        const s = isLeft ? -1 : 1; 
        ctx.beginPath();
        ctx.moveTo(0, 0);
        // Curve stretching downwards and outwards
        ctx.bezierCurveTo(s * w * 0.05, h * 0.5, s * w * 0.15, h * 1.2, s * w * 0.25, h * 1.8);
        // Inner cut for the ribbon end
        ctx.lineTo(s * w * 0.1, h * 1.5);
        ctx.lineTo(s * w * 0.02, h * 1.9);
        // Curve back up to knot
        ctx.bezierCurveTo(s * w * 0.02, h * 1.2, 0, h * 0.5, 0, 0);
        ctx.fill();
        
        // Turn off shadow for inside strokes to avoid blurry line artifacts
        ctx.shadowColor = "transparent";
        ctx.stroke();
        
        // Simple fabric crease line along ribbon
        ctx.beginPath();
        ctx.moveTo(s * w * 0.05, h * 0.4);
        ctx.lineTo(s * w * 0.08, h * 1.2);
        ctx.stroke();
        ctx.shadowColor = shadowColorOverlay; // restore for next piece
    }
    drawRibbon(true);
    drawRibbon(false);

    // Draw Bow Loops (Middle layer)
    function drawLoop(isLeft) {
        const s = isLeft ? -1 : 1;
        ctx.beginPath();
        ctx.moveTo(0, 0);
        ctx.bezierCurveTo(s * w * 0.15, -h * 0.8, s * w * 0.7, -h * 0.6, s * w * 0.5, 0);
        ctx.bezierCurveTo(s * w * 0.7, h * 0.6, s * w * 0.15, h * 0.8, 0, 0);
        ctx.fill();
        
        ctx.shadowColor = "transparent";
        ctx.stroke();

        // Inner folding creases to simulate pinched fabric converging on the knot
        ctx.beginPath();
        ctx.moveTo(s * w * 0.1, 0);
        ctx.quadraticCurveTo(s * w * 0.2, 0, s * w * 0.35, h * 0.1);
        ctx.stroke();

        ctx.beginPath();
        ctx.moveTo(s * w * 0.1, -h * 0.05);
        ctx.quadraticCurveTo(s * w * 0.2, -h * 0.1, s * w * 0.35, -h * 0.2);
        ctx.stroke();
        ctx.shadowColor = shadowColorOverlay;
    }
    drawLoop(true);
    drawLoop(false);

    // Draw the Central Knot (Front layer)
    ctx.beginPath();
    ctx.ellipse(0, 0, w * 0.1, h * 0.25, 0, 0, 2 * Math.PI);
    ctx.fill();
    ctx.shadowColor = "transparent";
    ctx.stroke();

    // Creases on the central knot
    ctx.beginPath();
    ctx.moveTo(-w * 0.03, -h * 0.15);
    ctx.quadraticCurveTo(w * 0.03, 0, -w * 0.03, h * 0.15);
    ctx.stroke();
    
    ctx.beginPath();
    ctx.moveTo(w * 0.02, -h * 0.1);
    ctx.quadraticCurveTo(-w * 0.01, 0, w * 0.02, h * 0.1);
    ctx.stroke();

    // Reset Context and Return
    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 Image New Bow Adder is a tool that allows you to overlay a procedurally generated, decorative bow onto any image. You can customize the bow’s appearance by choosing a specific color, adjusting its size, and positioning it anywhere on the image. It also offers rotation controls to ensure the bow fits perfectly within your composition. This tool is ideal for creating festive graphics, decorating product photos for gifts, or adding a celebratory touch to personal photographs.

Leave a Reply

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