Technical

PNG, JPG, or WebP? Choosing the Right Format for Text Images

By Tu Luu · · · 13 min read

Most format advice online is written for photographs, which is why it gives you the wrong answer for text. An image full of sharp letter edges behaves almost exactly opposite to a photo of a sunset, and picking the format by habit is how you end up with muddy, grey-fringed lettering.

The one distinction that decides everything

Every image format is either lossless or lossy. Lossless means the pixels you get back are bit-for-bit the pixels you put in. Lossy means the encoder is allowed to throw information away to make the file smaller, and it decides what to discard based on assumptions about what human eyes ignore.

Those assumptions were built around photographs. Photos are full of gradual tonal shifts, so a lossy encoder can smooth out fine detail and nobody notices. Text is the opposite: it is defined almost entirely by hard, high-contrast edges. Every single letter is a boundary between two very different colours, and edges are precisely what lossy compression handles worst.

Why JPG ruins text specifically

JPG compresses by splitting the image into 8×8 pixel blocks and describing each block as a sum of wave patterns, then discarding the subtlest waves. Reproducing a crisp black-to-white transition requires a lot of those subtle waves. Take them away and the encoder approximates the edge with a ripple instead of a clean step.

Side-by-side magnified comparison of the words Sharp edges rendered as a lossless PNG at 13.6 KB and as a JPEG at quality 40 and 5.8 KB. The PNG letter edges are clean; the JPEG shows grey ringing halos in the white space around every stroke.
The same text encoded both ways, magnified so the pixels show. The JPEG is the smaller file here — that is the trade, not an argument against it. What the extra PNG bytes buy is edges that stay where they were drawn.

You see that ripple as ringing: faint grey or coloured ghosting that echoes outward from every letter, most visible against flat backgrounds. At small text sizes the ripples from neighbouring letters overlap and the whole line takes on a dirty haze. Zoom into any JPG screenshot of a document and you will find it immediately.

JPG makes this worse by storing colour at half resolution in each direction (chroma subsampling). Coloured text on a coloured background — red on white, white on blue — gets its edges smeared before block compression even begins. This is why red text is the classic worst case for JPG.

The damage compounds. JPG is lossy every time it is saved. If you screenshot a JPG, crop it, and save again, you are compressing artefacts that are already there. Text images often get re-saved many times as they move between apps, and each round trip degrades them further. Lossless formats have no such generational loss.

Why PNG is the sane default for text

PNG is lossless, so a black letter stays exactly black and the pixel beside it stays exactly white. But the more interesting property is why PNG files stay small for this kind of image.

PNG compresses by finding repetition. A text image is mostly one flat background colour, which is about the most repetitive data possible, so it collapses dramatically. A 1200×600 PNG of a quote on a solid background often lands under 30 KB. The same image as a photograph would be hundreds of kilobytes.

This produces a result that surprises people: for text on a flat background, the lossless format is the smaller file. The usual trade-off does not apply. On the densest sample I measured — a 618×213 invoice listing at 14px — lossless WebP came to 4.9 KB against 103 KB for JPEG at quality 85. That is not a marginal win, it is twenty times.

It reverses on screenshots, though, and I only found that out by measuring it. The numbers and the reason are further down.

PNG also supports a full alpha channel, which is what lets you export text with a genuinely transparent background for overlays and watermarks. JPG has no concept of transparency at all — it will fill it with white or black.

Where WebP actually wins

WebP is often described as simply better than both, which oversells it. WebP has two separate modes and they behave very differently:

  • Lossy WebP is a modernised JPG. It handles edges noticeably better than JPG and supports transparency, but it is still throwing information away and still softens fine lettering.
  • Lossless WebP is a modernised PNG, and it genuinely does beat PNG on size. Across 29 samples the median saving was 28% for the same exact pixels — but the spread ran from 9% to 48%, and fewer than half the samples landed inside the 20–30% band this article used to quote.

So the honest summary is: lossless WebP is the technically superior choice for text images, and PNG is the choice that works everywhere. If you are publishing to your own website and control the pipeline, lossless WebP saves real bandwidth. If the file is going to be pasted into a chat app, dropped into a slide deck, emailed to a colleague, or opened by someone on an older system, PNG will not make you troubleshoot anything.

What I measured, and where I was wrong

Both of the size claims above used to sit here with no source behind them. I had repeated them because they are what everyone repeats. So I measured: 29 text images, six encoders, every byte count for the same exact pixels. The harness is in the repository as scripts/webp-benchmark.mjs, and the full table of results is checked in beside it, because a benchmark you cannot re-run is just a number to trust.

One claim survived, one did not, and the most useful finding was not either of them.

1. "20–30% smaller than PNG" — roughly right, much wider than stated

Median saving across all 29 samples was 28.3%, so the old figure was not wrong so much as falsely precise. The real range was 9% to 48%, and only 12 of 29 samples fell inside the band. The saving is largest on complex images and smallest on the flat two-colour text where PNG is already near-optimal.

The bigger catch is what "PNG" means. That 28% is against a PNG already squeezed with oxipng at maximum effort. Against the PNG a browser actually hands you, the median saving is 73% — because the browser's own PNG encoder is the weakest link in the chain, not the format.

2. "For text images the lossless format is usually smaller" — half wrong

Kind of imageLossless beat JPEG q85
Rendered text on flat colour13 of 13
Text over a background of rising complexity5 of 10
Real UI screenshots0 of 4

Every screenshot lost, and lost clearly — JPEG at quality 85 came out around 1.4× smaller than the best lossless encoding of the same capture. The old advice in the decision table below, that screenshots are "the worst case for JPG", was simply not true of the four real screenshots on this site.

Finding the reason took three tries, and the first two were wrong. My first guess was the number of distinct colours, so I built a sweep holding the text fixed and varying only the background — and lossless kept winning up to 4,500 colours, which killed the theory. My second guess was that screenshots are mostly empty space, so I swept the percentage of the canvas covered in text from 5% to 100% — lossless won every single one, which killed that theory too.

What actually matters is whether the non-text area is flat or continuously varying. My first sweep used hard-edged bands, and a band of one colour compresses by repetition no matter how many bands the image contains; once I made the background a true per-column ramp, the crossover appeared immediately, between roughly 3,900 and 5,600 distinct colours. Real screenshots are full of soft shadows, subtle panel gradients and anti-aliased chrome — continuous variation everywhere, which is exactly what lossless compression cannot exploit and what JPEG discards cheaply.

So the rule worth carrying away is not about text at all: if the background is flat, lossless wins by a mile; if it varies smoothly, JPEG wins even though the image is full of text.

3. The finding I was not looking for: the browser's PNG encoder

Chrome's canvas.toBlob writes PNGs a median of 2.1× larger than the same pixels through oxipng, and on the dense invoice sample it was 32.8× larger — 167 KB where 5 KB was enough. Its WebP encoder is 2× off cwebp too. This matters here more than anywhere, because that encoder is what this site's own tools call when you press download.

And it applied to this page. The four screenshots in the guides on this site were carrying 42% to 57% more bytes than they needed to, so they are now lossless WebP — including the JPEG-artefact comparison above, whose pixels were checked byte for byte against the original before the PNG was deleted.

Note that this is not what the table below recommends for a screenshot with gradients in it: by size alone, JPEG would have won on three of the four. I did not take that trade, for two reasons worth being explicit about. One of these images exists specifically to show what JPEG does to lettering, so encoding it as JPEG would be absurd. And this benchmark measured bytes, not how the result looks — a size result is not licence to make a quality decision it never tested.

The practical version for anyone exporting images: the format argument matters less than running the file through an optimiser afterwards. Switching PNG to lossless WebP saves about 28%. Switching a browser-exported PNG to an optimised one saves about 50%, before you change format at all.

Method, and its limits. 29 samples: text rendered by Canvas, two controlled sweeps, and four real UI screenshots. Encoders: Chrome 151 canvas PNG/WebP/JPEG, oxipng 10.1.1 at -o max with and without zopfli, and cwebp 1.6.0 at -lossless -z 9. Every lossless output was decoded outside the browser and compared byte for byte against the source — 145 of 145 matched exactly. That verification cannot be done through a canvas: reading pixels back with getImageData round-trips through premultiplied alpha and shifts anti-aliased edges by one to three levels, which looks exactly like encoder loss and is not. An earlier draft of this benchmark reported precisely that false result. No photographs were tested, and encode time — which for cwebp -z 9 is substantial — was not measured.

A decision table you can actually use

What the image containsUseWhy
Text on a solid backgroundPNGCrisp edges, and usually the smaller file anyway
Text needing transparencyPNGFull alpha channel; JPG cannot do it at all
Screenshot of a document or plain UIPNG or lossless WebPFlat panels compress by repetition; nothing to lose
Screenshot with shadows, gradients or chromeJPG q85 — measured, not assumedLossless lost all 4 real captures I tested, by about 1.4×
Text images for your own siteLossless WebPSame pixels as PNG, median 28% less bandwidth
Anything a browser exportedRun it through an optimiserMedian 2.1× larger than it needs to be, worst case 32.8×
A photo with a caption burned inHigh-quality JPG or lossy WebPPhoto content dominates; keep quality ≥ 85
Anything being archived or re-editedPNGNo generational loss across repeated saves

Formats worth mentioning briefly

GIF is lossless but capped at 256 colours. For plain two-colour text it technically works, and it fails as soon as you use anti-aliased edges or a gradient background, since those need far more than 256 shades. Its transparency is also binary — a pixel is fully opaque or fully invisible — which produces jagged letter edges. There is no reason to choose it over PNG today.

SVG is the genuinely best option when it fits, because it stores the text as vector instructions rather than pixels, so it stays razor sharp at any size and the file is tiny. The catch is that it only renders correctly where the font is available, and many platforms — most social networks among them — will not accept an SVG upload at all. That is exactly why rasterising to PNG remains the practical route for social graphics.

AVIF compresses better than WebP but is slower to encode and its lossless mode is less consistently supported. For text specifically, lossless WebP is the better-supported pick right now.

Three mistakes worth avoiding

Converting a JPG to PNG to "fix" it. Once compression artefacts are baked into the pixels, they are part of the image. PNG will faithfully preserve the damage. Always re-export from the original source instead.

Exporting text at display size. Whatever the format, generate at roughly twice the size you intend to display, so it stays sharp on high-density screens. Downscaling looks fine; upscaling never does.

Assuming the platform keeps your format. Most social networks re-encode every upload to their own lossy format regardless of what you send. You cannot prevent that, but uploading a clean lossless original means their encoder starts from undamaged pixels rather than compressing artefacts twice.

Try it yourself

The clearest way to understand any of this is to see it. Generate the same line of text twice with our Text to Image Converter, save one copy as PNG, run the other through any JPG compressor at moderate quality, then zoom both to 400%. The comparison near the top of this page is exactly that test already run — but doing it on your own text takes about ten seconds, and seeing the grey haze appear around your own letters is more convincing than being told about it.