Please bookmark this page to avoid losing your image tool!

Image Excavator Tool

(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, initialBucketSize = "50", soilStyle = "dark") {
    // Create main container
    const container = document.createElement('div');
    container.style.fontFamily = '"Segoe UI", Tahoma, Geneva, Verdana, sans-serif';
    container.style.display = 'flex';
    container.style.flexDirection = 'column';
    container.style.alignItems = 'center';
    container.style.gap = '15px';
    container.style.padding = '20px';
    container.style.backgroundColor = '#2c3e50';
    container.style.color = '#ecf0f1';
    container.style.border = '5px solid #e67e22';
    container.style.borderRadius = '12px';
    container.style.maxWidth = '100%';
    container.style.boxSizing = 'border-box';

    // Title and Instructions
    const title = document.createElement('h2');
    title.innerText = '🚜 Image Excavator Tool';
    title.style.margin = '0';
    title.style.color = '#f1c40f';
    container.appendChild(title);

    const instructions = document.createElement('p');
    instructions.innerText = 'Копательная машина: Click and drag over the image to excavate holes and reveal the ground underneath!';
    instructions.style.margin = '0';
    instructions.style.textAlign = 'center';
    container.appendChild(instructions);

    // Toolbar
    const toolbar = document.createElement('div');
    toolbar.style.display = 'flex';
    toolbar.style.flexWrap = 'wrap';
    toolbar.style.gap = '10px';
    toolbar.style.alignItems = 'center';
    toolbar.style.justifyContent = 'center';
    container.appendChild(toolbar);

    // Size Slider
    const sizeLabel = document.createElement('label');
    sizeLabel.innerText = 'Bucket Size:';
    sizeLabel.style.fontWeight = 'bold';
    
    const sizeInput = document.createElement('input');
    sizeInput.type = 'range';
    sizeInput.min = '10';
    sizeInput.max = '200';
    sizeInput.value = initialBucketSize;
    
    toolbar.appendChild(sizeLabel);
    toolbar.appendChild(sizeInput);

    // Buttons styling helper
    const styleButton = (btn) => {
        btn.style.padding = '8px 12px';
        btn.style.backgroundColor = '#e67e22';
        btn.style.color = '#fff';
        btn.style.border = 'none';
        btn.style.borderRadius = '4px';
        btn.style.cursor = 'pointer';
        btn.style.fontWeight = 'bold';
        btn.onmouseover = () => btn.style.backgroundColor = '#d35400';
        btn.onmouseout = () => btn.style.backgroundColor = '#e67e22';
    };

    // Auto Dig Button
    const autoDigBtn = document.createElement('button');
    autoDigBtn.innerText = 'Auto Excavate';
    styleButton(autoDigBtn);
    toolbar.appendChild(autoDigBtn);

    // Reset Button
    const resetBtn = document.createElement('button');
    resetBtn.innerText = 'Refill (Reset)';
    styleButton(resetBtn);
    toolbar.appendChild(resetBtn);

    // Export Button
    const exportBtn = document.createElement('button');
    exportBtn.innerText = 'Export Image';
    styleButton(exportBtn);
    toolbar.appendChild(exportBtn);

    // Canvas Container (simulates the underground soil)
    const canvasContainer = document.createElement('div');
    canvasContainer.style.position = 'relative';
    canvasContainer.style.boxShadow = 'inset 0 0 20px rgba(0,0,0,0.8), 0 10px 20px rgba(0,0,0,0.5)';
    canvasContainer.style.borderRadius = '4px';
    canvasContainer.style.overflow = 'hidden';
    
    // Procedural Dirt Background using CSS gradients
    const bgColor = soilStyle === 'dark' ? '#271911' : '#5D4037';
    const highlightColor = soilStyle === 'dark' ? '#3e2723' : '#795548';
    canvasContainer.style.backgroundColor = bgColor;
    canvasContainer.style.backgroundImage = `
        radial-gradient(circle at 20% 30%, ${highlightColor} 2px, transparent 4px),
        radial-gradient(circle at 70% 60%, ${highlightColor} 3px, transparent 5px),
        radial-gradient(circle at 40% 80%, ${highlightColor} 2px, transparent 4px),
        radial-gradient(circle at 80% 20%, ${highlightColor} 4px, transparent 6px)
    `;
    canvasContainer.style.backgroundSize = '50px 50px';
    canvasContainer.style.cursor = 'crosshair';

    // The Working Canvas
    const canvas = document.createElement('canvas');
    canvas.width = originalImg.width;
    canvas.height = originalImg.height;
    canvas.style.display = 'block';
    canvas.style.maxWidth = '100%';
    canvas.style.height = 'auto'; // Maintain aspect ratio when scaling down
    canvas.style.touchAction = 'none'; // Prevent scrolling while touching

    canvasContainer.appendChild(canvas);
    container.appendChild(canvasContainer);

    const ctx = canvas.getContext('2d');

    // Function to initialize/reset the image
    const drawOriginal = () => {
        ctx.globalCompositeOperation = 'source-over';
        ctx.drawImage(originalImg, 0, 0);
    };
    drawOriginal();

    let isDigging = false;
    let autoDigInterval = null;

    // The core "Dig" function (makes pixels transparent based on bucket shape)
    const dig = (x, y) => {
        const size = parseInt(sizeInput.value, 10);
        ctx.globalCompositeOperation = 'destination-out';
        
        ctx.beginPath();
        
        // Main bucket bite (roughly square)
        ctx.rect(x - size / 2, y - size / 2, size, size);
        
        // Bucket teeth (jagged edges at the bottom)
        const teeth = 5;
        const toothWidth = size / teeth;
        for (let i = 0; i < teeth; i++) {
            ctx.rect((x - size / 2) + (i * toothWidth), y + size / 2, toothWidth * 0.7, size / 2.5);
        }
        ctx.fill();

        // Scattered dirt/rubble around the bite
        for (let i = 0; i < 15; i++) {
            const rx = x + (Math.random() - 0.5) * size * 2.2;
            const ry = y + (Math.random() - 0.5) * size * 2.2;
            const rs = Math.random() * (size / 6);
            ctx.beginPath();
            ctx.arc(rx, ry, rs, 0, Math.PI * 2);
            ctx.fill();
        }

        ctx.globalCompositeOperation = 'source-over';
    };

    // Calculate scaling if canvas is constrained by CSS max-width
    const getCoords = (e) => {
        const rect = canvas.getBoundingClientRect();
        const scaleX = canvas.width / rect.width;
        const scaleY = canvas.height / rect.height;
        let clientX = e.clientX;
        let clientY = e.clientY;
        
        if (e.touches && e.touches.length > 0) {
            clientX = e.touches[0].clientX;
            clientY = e.touches[0].clientY;
        }
        
        return {
            x: (clientX - rect.left) * scaleX,
            y: (clientY - rect.top) * scaleY
        };
    };

    // Mouse & Touch Event Handlers
    const onStart = (e) => {
        if(e.cancelable) e.preventDefault();
        clearInterval(autoDigInterval);
        isDigging = true;
        const { x, y } = getCoords(e);
        dig(x, y);
    };

    const onMove = (e) => {
        if (!isDigging) return;
        if(e.cancelable) e.preventDefault();
        const { x, y } = getCoords(e);
        dig(x, y);
    };

    const onEnd = () => {
        isDigging = false;
    };

    // Attach listeners
    canvas.addEventListener('mousedown', onStart);
    canvas.addEventListener('mousemove', onMove);
    canvas.addEventListener('mouseup', onEnd);
    canvas.addEventListener('mouseleave', onEnd);

    canvas.addEventListener('touchstart', onStart, { passive: false });
    canvas.addEventListener('touchmove', onMove, { passive: false });
    canvas.addEventListener('touchend', onEnd);
    canvas.addEventListener('touchcancel', onEnd);

    // Button event listeners
    resetBtn.addEventListener('click', () => {
        clearInterval(autoDigInterval);
        drawOriginal();
    });

    autoDigBtn.addEventListener('click', () => {
        clearInterval(autoDigInterval); // Reset if already running
        
        let x = canvas.width / 2;
        let y = canvas.height / 2;
        let step = 0;
        const maxSteps = 40;

        autoDigInterval = setInterval(() => {
            dig(x, y);
            const size = parseInt(sizeInput.value, 10);
            
            // Random walk simulating a wandering excavator machine
            x += (Math.random() - 0.5) * size * 1.5;
            y += (Math.random() - 0.5) * size * 1.5;
            
            // Keep machine in bounds
            x = Math.max(size, Math.min(canvas.width - size, x));
            y = Math.max(size, Math.min(canvas.height - size, y));
            
            step++;
            if (step >= maxSteps) {
                clearInterval(autoDigInterval);
            }
        }, 60);
    });

    exportBtn.addEventListener('click', () => {
        const link = document.createElement('a');
        link.download = 'excavated-image.png';
        link.href = canvas.toDataURL('image/png');
        link.click();
    });

    return container;
}

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 Excavator Tool is an interactive image editing utility that allows users to virtually ‘dig’ through an image to reveal what is underneath. By clicking and dragging over an uploaded image, users can simulate an excavation process, creating holes and textures that mimic a digging machine’s movement. The tool features adjustable bucket sizes, an ‘Auto Excavate’ function for automated digging patterns, and a reset option to refill the image. This tool is ideal for creating interactive digital scratch-off effects, engaging educational content, or fun, gamified visual experiences for websites and presentations.

Leave a Reply

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