bulk watermark

Bulk Watermark Images with Drag and Drop

Bulk Watermark Images with Ease

A powerful web tool to watermark, rename, resize, and convert images in bulk with drag-and-drop. Features include watermark customization, multiple resize modes, preview, progress bar, and ZIP download with a confirmation modal.

Why Watermark Your Images?

Watermarking images protects your intellectual property, promotes your brand, and adds a professional touch to your visuals. This tool streamlines bulk processing for efficiency.

Features

  • Drag and Drop: Upload multiple images with visual feedback.
  • Watermark: Customize text, opacity, color, font size, and position (top/middle/bottom, left/center/right).
  • Rename: Use custom prefixes with sequential numbering.
  • Resize Options:
    • Exact: Set width/height, maintain aspect if one provided, stretch if both.
    • Contain: Fit within dimensions with transparent padding.
    • Cover: Fill dimensions with cropping.
    • Percentage: Scale by percentage (10-200%).
    • File Size: Target approximate file size (e.g., 100KB, 1MB).
    • None: Keep original dimensions.
  • Output Format: PNG, JPEG, or WebP.
  • Preview: View the first image with applied settings.
  • Progress Bar: Track processing progress.
  • ZIP Download: Download all images in a ZIP file with a user-friendly confirmation modal.

Try It Now

Drag and drop your images here or click to select files.

Preview (First Image)

How the Tool Works

Built with HTML5 Canvas, JavaScript, and JSZip, this tool processes images client-side. It includes advanced resize algorithms, file size optimization, and a user-friendly download confirmation modal.


// Helper functions
function hexToRgb(hex) {
    const result = /^#?([a-f\\d]{2})([a-f\\d]{2})([a-f\\d]{2})$/i.exec(hex);
    return result ? {
        r: parseInt(result[1], 16),
        g: parseInt(result[2], 16),
        b: parseInt(result[3], 16)
    } : { r: 255, g: 255, b: 255 };
}

function getWatermarkPosition(position, width, height, padding) {
    let x, y, align, baseline;
    switch (position) {
        case 'top-left': align = 'left'; baseline = 'top'; x = padding; y = padding; break;
        case 'top-center': align = 'center'; baseline = 'top'; x = width / 2; y = padding; break;
        case 'top-right': align = 'right'; baseline = 'top'; x = width - padding; y = padding; break;
        case 'middle-left': align = 'left'; baseline = 'middle'; x = padding; y = height / 2; break;
        case 'middle-center': align = 'center'; baseline = 'middle'; x = width / 2; y = height / 2; break;
        case 'middle-right': align = 'right'; baseline = 'middle'; x = width - padding; y = height / 2; break;
        case 'bottom-left': align = 'left'; baseline = 'bottom'; x = padding; y = height - padding; break;
        case 'bottom-center': align = 'center'; baseline = 'bottom'; x = width / 2; y = height - padding; break;
        case 'bottom-right': align = 'right'; baseline = 'bottom'; x = width - padding; y = height - padding; break;
    }
    return { x, y, align, baseline };
}

async function adjustToFileSize(img, targetSizeKB, format, watermarkText, watermarkParams, maxIterations = 10) {
    let quality = 0.9;
    let step = 0.1;
    let iterations = 0;
    let blob;
    const { position, opacity, rgb, fontSize } = watermarkParams;

    while (iterations < maxIterations) {
        const canvas = document.createElement('canvas');
        const ctx = canvas.getContext('2d');
        canvas.width = img.width;
        canvas.height = img.height;
        ctx.drawImage(img, 0, 0);

        if (watermarkText) {
            ctx.font = \`${fontSize}px Arial\`;
            ctx.fillStyle = \`rgba(\${rgb.r}, \${rgb.g}, \${rgb.b}, \${opacity})\`;
            const pos = getWatermarkPosition(position, canvas.width, canvas.height, 20);
            ctx.textAlign = pos.align;
            ctx.textBaseline = pos.baseline;
            ctx.fillText(watermarkText, pos.x, pos.y);
        }

        blob = await new Promise(resolve => canvas.toBlob(resolve, format, quality));
        if (!blob) throw new Error('Failed to create blob');
        const sizeKB = blob.size / 1024;

        if (Math.abs(sizeKB - targetSizeKB) < 10 || iterations === maxIterations - 1) break;
        if (sizeKB > targetSizeKB) quality -= step;
        else quality += step;
        step /= 2;
        iterations++;
    }
    return blob;
}

// Core logic in updatePreview and processImages (see script below)
            
Search
Popular Now
Loading

Signing-in 3 seconds...

Signing-up 3 seconds...