Optimise It: Image Compression Widget

This custom widget is a privacy-first, client-side image optimisation tool built using native HTML5 and JavaScript. Instead of relying on external servers or third-party services to process files, it utilises the browser's File and Canvas APIs to load, resize, and compress images entirely on the user's local device.

When a visitor selects a photo, the widget renders it onto an invisible digital canvas, applying built-in bilinear resampling to cleanly downscale the dimensions while preserving high visual fidelity. Because the image data never leaves the user's computer or smartphone, it guarantees absolute privacy, making it a highly secure solution for managing media.

From a user experience standpoint, the widget is designed to be lightweight, intuitive, and highly transparent. It provides straightforward controls that allow users to dictate their maximum preferred width and adjust a compression quality slider to balance file size with image sharpness.

Once the process button is clicked, the tool instantly generates the new image and displays a preview alongside exact file size statistics, clearly showing the percentage of data saved. The user can then immediately download the optimised JPEG directly to their device, resulting in a seamless, lightning-fast workflow that requires no external software installations. 

Privacy-First Image Optimiser

All processing happens instantly on your device. Your images are never sent to a server.

IMAGE OPTIMISER V1.0
<div id="image-compressor-widget" style="font-family: Arial, sans-serif; padding: 15px; border: 1px solid #ccc; border-radius: 8px; max-width: 400px; background: #fafafa;">
    <h3 style="margin-top: 0;">Privacy-First Image Optimiser</h3>
    <p style="font-size: 12px; color: #666;">All processing happens instantly on your device. Your images are never sent to a server.</p>
    
    <label for="imageLoader" style="display: block; margin-bottom: 10px; font-weight: bold;">Select an Image:</label>
    <input type="file" id="imageLoader" accept="image/*" style="width: 100%; margin-bottom: 15px;" />

    <div id="controls" style="display:none; margin-bottom: 15px;">
        <label for="maxWidth" style="display: block; font-size: 14px; margin-bottom: 5px;">Max Width (px):</label>
        <input type="number" id="maxWidth" value="1200" style="width: 100%; padding: 5px; margin-bottom: 10px; box-sizing: border-box;" />

        <label for="quality" style="display: block; font-size: 14px; margin-bottom: 5px;">Quality (10-100%):</label>
        <input type="range" id="quality" min="10" max="100" value="80" style="width: 100%; margin-bottom: 5px;" />
        <span id="qualityVal" style="font-size: 12px; color: #555;">80%</span>
        
        <button id="processBtn" style="width: 100%; margin-top: 15px; padding: 10px; background-color: #007bff; color: white; border: none; border-radius: 4px; cursor: pointer; font-weight: bold;">Resize & Compress</button>
    </div>

    <div id="result" style="display:none; text-align: center; margin-top: 15px; border-top: 1px solid #eee; padding-top: 15px;">
        <p id="stats" style="font-size: 13px; color: #333; margin-bottom: 10px;"></p>
        <img id="preview" style="max-width: 100%; height: auto; border-radius: 4px; margin-bottom: 10px; box-shadow: 0 2px 4px rgba(0,0,0,0.1);" />
        <br/>
        <a id="downloadLink" download="optimized-image.jpg" style="display: inline-block; padding: 8px 15px; background-color: #28a745; color: white; text-decoration: none; border-radius: 4px; font-weight: bold;">Download Optimised Image</a>
    </div>
</div>

<script>
document.getElementById('imageLoader').addEventListener('change', handleImage, false);
document.getElementById('quality').addEventListener('input', function(e) {
    document.getElementById('qualityVal').textContent = e.target.value + '%';
});
document.getElementById('processBtn').addEventListener('click', processImage);

let originalImage = new Image();
let originalFileName = "optimized-image.jpg";
let originalSize = 0;

function handleImage(e) {
    const reader = new FileReader();
    const file = e.target.files[0];
    if (!file) return;
    
    originalFileName = "opt-" + file.name;
    originalSize = file.size;

    reader.onload = function(event) {
        originalImage.src = event.target.result;
        originalImage.onload = function() {
            document.getElementById('controls').style.display = 'block';
            if(originalImage.width < 1200) {
                document.getElementById('maxWidth').value = originalImage.width;
            }
        }
    }
    reader.readAsDataURL(file);
}

function processImage() {
    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');

    let targetWidth = parseInt(document.getElementById('maxWidth').value);
    let targetHeight = originalImage.height;

    if (originalImage.width > targetWidth) {
        targetHeight = Math.round((targetWidth / originalImage.width) * originalImage.height);
    } else {
        targetWidth = originalImage.width;
    }

    canvas.width = targetWidth;
    canvas.height = targetHeight;

    ctx.drawImage(originalImage, 0, 0, targetWidth, targetHeight);

    const qualitySetting = parseInt(document.getElementById('quality').value) / 100;

    canvas.toBlob(function(blob) {
        const url = URL.createObjectURL(blob);
        
        const preview = document.getElementById('preview');
        const downloadLink = document.getElementById('downloadLink');
        const stats = document.getElementById('stats');
        
        preview.src = url;
        downloadLink.href = url;
        downloadLink.download = originalFileName.replace(/\.[^/.]+$/, "") + ".jpg";
        
        const oldSizeKB = (originalSize / 1024).toFixed(1);
        const newSizeKB = (blob.size / 1024).toFixed(1);
        const savings = (((originalSize - blob.size) / originalSize) * 100).toFixed(0);

        stats.innerHTML = `<strong>Original:</strong> ${oldSizeKB} KB <br> <strong>Optimized:</strong> ${newSizeKB} KB (-${savings}%)`;
        
        document.getElementById('result').style.display = 'block';
    }, 'image/jpeg', qualitySetting);
}
</script>

Post a Comment

0 Comments

Notice:
Comments are moderated and may not appear immediately. Please keep your comments respectful, and relevant to the post. Spam will not be tolerated. My site. My rules.

Post a Comment (0)