DEV Community

Anton Reshetov
Anton Reshetov

Posted on

5 1

Compress image in browser? Easy!

First of all I want to thank people who are interested in the project MySigMail - UI Email Signature Generator.

In repo came an interesting PR where it was implemented a way to compress the image in the browser, without using the server.

I would like to share the code:

function compressImage (base64) {
  const canvas = document.createElement('canvas')
  const img = document.createElement('img')

  return new Promise((resolve, reject) => {
    img.onload = function () {
      let width = img.width
      let height = img.height
      const maxHeight = 200
      const maxWidth = 200

      if (width > height) {
        if (width > maxWidth) {
          height = Math.round((height *= maxWidth / width))
          width = maxWidth
        }
      } else {
        if (height > maxHeight) {
          width = Math.round((width *= maxHeight / height))
          height = maxHeight
        }
      }
      canvas.width = width
      canvas.height = height

      const ctx = canvas.getContext('2d')
      ctx.drawImage(img, 0, 0, width, height)

      resolve(canvas.toDataURL('image/jpeg', 0.7))
    }
    img.onerror = function (err) {
      reject(err)
    }
    img.src = base64
  })
}
Enter fullscreen mode Exit fullscreen mode

Thanks @mykeels

MySigMail always happy to have contributors :)

Now I am working on version 2 in which there will be a WYSIWYG email editor.

Therefore, I would be happy if there are willing to help me in the development of templates for signatures in version 1.

https://github.com/antonreshetov/mysigmail

Sentry blog image

How I fixed 20 seconds of lag for every user in just 20 minutes.

Our AI agent was running 10-20 seconds slower than it should, impacting both our own developers and our early adopters. See how I used Sentry Profiling to fix it in record time.

Read more

Top comments (11)

Collapse
 
qm3ster profile image
Mihail Malo

Just a note, the width > height check only works correctly when maxHeight === maxWidth:

{
      let width = 200
      let height = 200
      const maxHeight = 200
      const maxWidth = 10

      if (width > height) {
        if (width > maxWidth) {
          height = Math.round((height *= maxWidth / width))
          width = maxWidth
        }
      } else {
        if (height > maxHeight) {
          width = Math.round((width *= maxHeight / height))
          height = maxHeight
        }
      }
console.log(width,height)
}
{
      let width = 201
      let height = 200
      const maxHeight = 10
      const maxWidth = 200

      if (width > height) {
        if (width > maxWidth) {
          height = Math.round((height *= maxWidth / width))
          width = maxWidth
        }
      } else {
        if (height > maxHeight) {
          width = Math.round((width *= maxHeight / height))
          height = maxHeight
        }
      }
console.log(width,height)
}
Collapse
 
lexlohr profile image
Alex Lohr

It's more resize than compress. If you wanted compression, try something like squoosh.app - transpiling image codecs and running them in the front end.

Collapse
 
antonreshetov profile image
Anton Reshetov

Why only resize? This is resize and compression, though not some advanced as squoosh. But the goals of the application is enough

canvas.toDataURL('image/jpeg', 0.7) 0.7 - is compression rank

developer.mozilla.org/en-US/docs/W...

Collapse
 
lexlohr profile image
Alex Lohr

Actually, it is a percentage of signal quality for the quantizer to aim for. While the result is compression, what you set there is the amount of signal loss you deem acceptable.

Thread Thread
 
antonreshetov profile image
Anton Reshetov

I can not confirm your words, but I trust the documentation

A Number between 0 and 1 indicating the image quality to use for image formats that use lossy compression such as image/jpeg and image/webp.
If this argument is anything else, the default value for image quality is used. The default value is 0.92. Other arguments are ignored.

Thread Thread
 
lexlohr profile image
Alex Lohr

That is exactly what I was referring to: you set the quality of the image in a lossy codec, not the compression factor (though both might coincide).

Thread Thread
 
antonreshetov profile image
Anton Reshetov

Got it )

Collapse
 
rhymes profile image
rhymes

I wonder if this would be the perfect thing to delegate to WebAssembly, what do you think? It might become slow with big images (also it's going to happen in the calling thread, right?)

Collapse
 
antonreshetov profile image
Anton Reshetov

Sadly, but i'am dummy in WebAssembly 😬 So I can't say anything about that.

Collapse
 
rhymes profile image
rhymes

Well, it's not like I'm an expert either. But we can learn things 😉

In the meantime you can test what happens to the function if you give it a truly massive image

Collapse
 
qm3ster profile image
Mihail Malo

Definitely

Cloudinary image

Optimize, customize, deliver, manage and analyze your images.

Remove background in all your web images at the same time, use outpainting to expand images with matching content, remove objects via open-set object detection and fill, recolor, crop, resize... Discover these and hundreds more ways to manage your web images and videos on a scale.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay