October 22, 2024
Chicago 12, Melborne City, USA
javascript

Performant image filesize decrease (via quality)


I need a way to decrease the file size of images (to 8kb specifically), and I’m wondering what the best method might be.

  • It can be within the least lines or the fastest (preferrably)
  • I’d love to maintain the image quality as best as possible, but it’s not the end of the world with lesser quality
  • The dimensions should stay as is (though for my case, I do usually fit them in 48×48 containers)

My current approach is to simply decrement image quality until under the limit.

Shown here –

pseudocode - fetching the image
image = fetch(source) -> Filereader.readAsDataUrl(responseBlob) // returns dataUrl
...
// Using a standard HTMLCanvas 
canvas.getContext('2d').drawImage(image, 0,0);
            
let q = 1; // Decrement in quality until under size limit
let imageData = canvas.toDataURL("image/webp", q)

while (imageData.length > 8000 && q != 0) {
    q -= 0.05;
    imageData = canvas.toDataURL("image/webp", q)
}
if (q) return imageData

But I’ve found there may be some better methods –

let response = fetch(originalImageSource)

let responseBlob = await response.blob()
let imageSize = responseBlob.size
if ((imagesize * 1.35) > 8000) { // 1.35* being the (upper bounds) size approximation of the resulting dataUrl
        let bitmap = await createImageBitmap(responseBlob)
    let canvas = new OffscreenCanvas(bitmap.width, bitmap.height)
    canvas.getContext("2d").drawImage(bitmap, 0, 0)

    let q = 1
    let imageBlob = await canvas.convertToBlob({type: "image/webp", quality: q})
    while (imageBlob.size > 8000 && q) {
        q -= 0.05
        imageBlob = await canvas.convertToBlob({type: "image/webp", quality: q})
    }
    
    
    return new Promise((resolve) => {
        let reader = new FileReader()
        reader.onloadend = () => {
            resolve(reader.result)   
        }
        reader.readAsDataURL(imageBlob)
    })

}

Is this even any better? I thought it might be, as I can run it in a service worker, and it doesn’t call canvas.toDataURL. but I’m not sure.
From my preliminary testing, the second method is a little faster. but with smaller images, it appears to be equal.

I’ve seen things like this – HTML5 Canvas Resize (Downscale) Image High Quality?, which I don’t quite understand, but could certainly try (if better than the above)

For my case,

  • the result must be a dataURL, as its stored permanently.

(Although, I’ve read that storing binary data in indexedDB may work too, though that’s not something I’m open to just yet) – https://stackoverflow.com/a/59025746

  • It should also be able to run in a service worker (but I can forgo that, if there’s an incredible DOM based approach or something)



You need to sign in to view this answers

Leave feedback about this

  • Quality
  • Price
  • Service

PROS

+
Add Field

CONS

+
Add Field
Choose Image
Choose Video