PNG, JPG and WebP Conversions, and the Browser Quirks Behind Them

You convert a logo from PNG to JPG for a form, and its see-through background comes back as a solid box. Or you convert your site’s images to WebP on an iPhone, and the “WebP” files turn out to be PNGs with the wrong name. ToolOnTap has four format converters, and all of them run on the same few lines of canvas code. Most of what’s interesting about them is how browsers behave at the edges, so that’s what this article is about.

One pipeline, four converters

PNG to JPG, JPG to PNG, WebP to JPG and Convert to WebP are the same tool with a different output format. Each image goes through three steps in the browser:

  1. Decode the file into pixels with createImageBitmap, which every current browser can do for JPG, PNG, WebP and GIF.
  2. Draw the pixels onto a canvas the size of the image.
  3. Encode the canvas in the new format with canvas.toBlob(callback, type, quality).

The browser’s own encoders do the real work, so no image library is downloaded for these pages. What I choose is the format and the quality:

Output Quality Kind of compression
JPG 0.92 Lossy
WebP 0.8 Lossy (the quality the tool passes)
PNG not used Lossless

0.92 is the default in my canvasToBlob helper. For WebP the converter passes 0.8, which is a common setting for images on the web. PNG ignores the quality argument, because there’s nothing to trade: every pixel is stored exactly.

Where the transparency goes

PNG, WebP and GIF can store transparency. JPG can’t: every pixel in a JPG has a color. So when a transparent image becomes a JPG, something has to fill the see-through areas.

If you leave it to the browser, the HTML standard tells it to put the image on a solid black background before encoding a format without transparency. A logo with black text on a transparent background then comes out as black on black. So before drawing, the tool paints the canvas white whenever the output is JPG:

// Paint white first when the format can't hold transparency
export function fillForFormat(ctx: CanvasRenderingContext2D, type: ImageFormat, width: number, height: number) {
  if (type !== 'image/jpeg') return
  ctx.fillStyle = '#fff'
  ctx.fillRect(0, 0, width, height)
}

White is a guess, but a safe one. Most logos are made for white pages and white documents, and a white box looks like a margin while a black one looks like a mistake.

Going the other way, transparency stays. PNG to WebP keeps transparent backgrounds transparent, because WebP supports them. JPG to PNG keeps the background the photo already had. The PNG can hold transparency, but there’s none in a JPG to carry across, and working out which pixels are “background” is a different job altogether.

Lossless doesn’t mean better

This is the part that confuses people most. PNG is lossless, so converting a JPG to PNG sounds like an upgrade. It isn’t one. The PNG stores the JPG’s pixels exactly, including every blur and block the JPG compression already made. Nothing comes back. What you get is a much bigger file, often several times the size of the JPG, because lossless compression is poor at photos.

There are good reasons to do it anyway. Some sites and icon tools only accept PNG. And if you’ll edit the image many times, saving each version as PNG stops new JPG damage from building up with every save.

The opposite conversion has its own surprise. PNG to JPG makes photos much smaller, but it can make graphics bigger. A screenshot or a flat-color chart compresses very well as PNG, while JPG adds soft halos around sharp edges and spends bytes on them. The tool shows the size change next to each file, so if a PNG grew as a JPG, keep the PNG.

WebP to JPG often grows too, for a simpler reason. WebP compresses more efficiently than JPG, so the same picture needs more bytes in the older format.

Safari can show WebP but can’t make it

Here’s the quirk that shaped Convert to WebP. When you ask a canvas for a format the browser can’t encode, the standard says it should hand back a PNG instead. There’s no error and no warning. The callback receives a perfectly good PNG blob.

Safari displays WebP images, but its canvas can’t encode them. On an iPhone or iPad, Chrome, Firefox and Edge are built on Safari’s engine too, so they behave the same way. Without a check, the tool would have saved PNG files named .webp. They’d often be bigger than the originals, and the website owner would never know.

So the converter checks what it got back:

async function convert(file: Blob, type: ImageFormat = 'image/jpeg'): Promise<ProcessResult> {
  const result = await renderImage(file, type, s => s, type === 'image/webp' ? 0.8 : undefined)
  if (result.blob.type !== type) throw new UnsupportedOutputError(type)
  return { blob: result.blob, note: result.scaled ? scaledNote(result) : undefined }
}

The blob’s type says which encoder actually ran. If it doesn’t match, nothing is saved. The file is shown with a message saying that this browser can’t create WebP images, and that Chrome, Edge or Firefox on a computer or an Android phone can.

The check looks at what the browser actually produced, so it needs no list of browser names. If Safari ever gains a WebP encoder, the tool will start working there without a change.

Smaller details

  • Animated GIF and WebP. Only the first frame is converted. JPG can’t animate, and the canvas only ever holds one frame.
  • Very large images. Results are capped at 16,777,216 pixels (4096 × 4096), the canvas size iOS Safari can handle, so a bigger image is scaled down and the file gets a note saying so.
  • Quality loss happens once. Each conversion decodes and encodes one time. Converting JPG to WebP and back to JPG costs quality twice, so keep your originals.

Try it

Pick the converter for the job: PNG to JPG for smaller photos, JPG to PNG for forms and editors that want PNG, WebP to JPG for images saved from websites, and Convert to WebP for your own site’s images. They all convert on your device, one file or a whole batch with a ZIP download. More tools are on the image tools page.

Tools in this article

  • Convert PNG to JPG

    Convert PNG images to JPG in your browser. Smaller files for photos, with transparent areas filled white. No upload, no sign-up.

  • Convert JPG to PNG

    Convert JPG or JPEG images to PNG right in your browser. Lossless output for editing, no upload and no sign-up.

  • Convert WebP to JPG

    Turn .webp images saved from websites into JPGs that older apps and upload forms accept. Batch convert in your browser, no upload.

  • Convert images to WebP

    Convert JPG, PNG and GIF images to WebP for faster websites, keeping transparency. Batch convert in your browser, no upload, no sign-up.

More articles