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)