Every one of the 37 JPGs I made here today has the same eleven markers in the same order: baseline sequential, three-component YCbCr at 4:2:0, a single scan, no restart markers, a JFIF header that carries no resolution, and a 456-byte sRGB profile. The quality setting changes exactly one thing — the quantization table — and it changes it by the formula printed in the JPEG specification, to the byte.
Short answer: you get the plainest, most widely readable JPEG there is. It is baseline sequential — FFC0, not the progressive FFC2 — with three components in YCbCr, luma sampled at 2×2 and both chroma planes at 1×1, which is what 4:2:0 means. One scan covers the whole image. There are no restart markers, no EXIF block, no Adobe marker, no comment, no embedded thumbnail, and no print resolution. The only thing the quality menu changes is the two quantization tables.
Those tables are not invented. They are the example tables printed in Annex K of the JPEG specification, scaled by the reference implementation's own formula: scale is 200 − 2 × quality, and each of the 64 entries becomes floor((entry × scale + 50) / 100), clamped to 1–255. I predicted all 64 entries of both tables at all three quality settings and compared them against what the site actually produced: 0 mismatches out of 64, every time, for luma and chroma alike. So the "95%" on the menu is a real JPEG quality of 95.
How I measured it: today, in Chrome 154.0.8037.99 (headless, Windows, window 1,280 × 900) against the live page. I ran 37 real conversions — 12 source files across the three quality settings — pulled every output back over the DevTools protocol as raw bytes, and parsed the markers locally. Zero conversions failed. The live page's HTML today is 13,951 bytes and was usable 1,985 ms after navigation.
This is the complete skeleton of a 38,078-byte output — a 700 × 476 photo at the default 0.85 setting. Sizes are the bytes the segment occupies in the file, marker and length field included.
| Marker | Bytes | What it holds |
|---|---|---|
FFD8 SOI | 2 | start of image |
FFE0 APP0 | 18 | JFIF 1.1, units 0, density 1 × 1, thumbnail 0 × 0 |
FFE2 APP2 | 474 | ICC profile, 456 bytes, chunk 1 of 1 |
FFDB DQT | 69 | luma quantization table (64 entries, 8-bit) |
FFDB DQT | 69 | chroma quantization table |
FFC0 SOF0 | 19 | baseline, 8-bit, 700 × 476, 3 components |
FFC4 DHT | 30 | DC table 0 — 9 symbols |
FFC4 DHT | 78 | AC table 0 — 57 symbols |
FFC4 DHT | 28 | DC table 1 — 7 symbols |
FFC4 DHT | 46 | AC table 1 — 25 symbols |
FFDA SOS | 14 | one scan, 3 components, Ss 0, Se 63, Ah/Al 0 |
FFD9 EOI | 2 | end of image — the last two bytes of the file |
All 37 files I produced had this exact sequence. Not one gained a segment, dropped one, or reordered one — not the 16 × 16 image, not the 4,032 × 3,024 one, not the monochrome ones, not the ones with an alpha channel. That uniformity is the finding: there is no "small file mode" or "big photo mode" hiding in here.
The frame header is FFC0. I looked for FFC2 — the progressive marker — in all 37 files and found it 0 times. The scan header says it plainly too: each file has exactly one FFDA, and its parameters are Ss = 0, Se = 63, Ah = 0, Al = 0 — one pass over all 64 coefficients, no successive approximation. That is the definition of baseline sequential.
Practically: the file decodes top to bottom in one pass and shows nothing until it is whole. Progressive JPEG would paint a rough version first, then sharpen it. Since the file never travels a network here, that costs you nothing.
The three components in the frame header read: component 1 (luma) sampling 2 × 2 against quant table 0, component 2 sampling 1 × 1 against table 1, component 3 sampling 1 × 1 against table 1. Two chroma planes at half the resolution of luma in both directions — 4:2:0. A minimum coded unit therefore covers a 16 × 16 block of pixels; the 700 × 476 photo is 1,320 MCUs, the 12-megapixel one is 47,628.
The converter never writes anything else. I fed it a genuinely monochrome HEIC — a real photo converted to a single channel before encoding — and it still came back as three components at 4:2:0, at 35,179 bytes. The same photo with its colour intact came back at 38,078 bytes. Stripping all the colour out saved 7.6%. At 12 megapixels the same comparison was 2,896,377 bytes against 3,177,680 — 8.9%.
There is a neat piece of evidence inside the file itself. Because the Huffman tables are rebuilt per image, a monochrome photo makes the chroma tables collapse: for that file, DC table 1 and AC table 1 each carry exactly 1 symbol. The chroma planes are so flat that one codeword describes them entirely — and the file still pays for them.
Two things I did not establish, so I am not going to claim them: I did not measure how much a single-component encode of the same pixels would have saved, and I could not isolate how much visible softening on hairline coloured edges comes from this JPEG's 4:2:0 rather than from the source HEIC, which is itself chroma-subsampled. Both would need an encoder path this site does not expose.
This was the part that surprised me by how exact it is. Take the luminance table in Annex K of the JPEG spec — the one that begins 16, 11, 10, 16, 24, 40, 51, 61 — apply the reference implementation's quality scaling, and you get precisely what the site wrote:
| Quality | Scale | Luma, first eight entries | Range | Chroma, first eight entries | Entries at the floor of 1 |
|---|---|---|---|---|---|
| 0.75 | 50 | 8 6 5 8 12 20 26 31 | 5 – 61 | 9 9 12 24 50 50 50 50 | 0 of 64 |
| 0.85 | 30 | 5 3 3 5 7 12 15 18 | 3 – 36 | 5 5 7 14 30 30 30 30 | 0 of 64 |
| 0.95 | 10 | 2 1 1 2 2 4 5 6 | 1 – 12 | 2 2 2 5 10 10 10 10 | 8 of 64 |
I ran the comparison over all 64 entries of both tables at all three settings: 0 mismatches. Every file at a given quality carried byte-identical tables, regardless of the image — all 37 outputs shared one table pair per setting.
At 0.95 eight of the luma entries bottom out at 1, the smallest step the format allows. Those coefficients are effectively being kept unquantized, which is why 0.95 costs so much more space for so little visible gain.
Each file carries four tables: DC and AC for luma, DC and AC for chroma. None of them is the standard table from the JPEG spec — I compared all four against the Annex K originals in all 37 files and the count of matches was 0. They are optimized tables, computed from the actual symbol frequencies of that particular image, which is the usual thing for a modern encoder to do and shaves a few percent off the size.
The symbol counts show the tables tracking the content, which is the clearest proof they are per-image:
| Source | DC luma | AC luma | DC chroma | AC chroma |
|---|---|---|---|---|
| Flat colour, no detail | 2 | 1 | 3 | 1 |
| 16 × 16 thumbnail | 3 | 18 | 2 | 8 |
| Monochrome photo | 9 | 61 | 1 | 1 |
| Colour photo (0.75) | 9 | 57 | 6 | 18 |
| Colour photo (0.85) | 9 | 57 | 7 | 25 |
| Colour photo (0.95) | 11 | 62 | 8 | 39 |
| 12-megapixel photo | 7 | 47 | 8 | 20 |
A flat image needs one AC codeword. A detailed photo at 0.95 needs 62. Nothing here is fixed boilerplate.
The absences are as consistent as the presences. Across all 37 files:
| Absent | Where it would appear | What it means for you |
|---|---|---|
| EXIF | FFE1 — 0 occurrences | no camera data, no GPS, no capture date, no orientation flag |
| Adobe marker | FFEE — 0 occurrences | no colour-transform hint; the file is plain YCbCr |
| Restart markers | FFDD — 0 occurrences | no error-resilience segmentation; a corrupt byte can take the rest of the scan with it |
| Comment | FFFE — 0 occurrences | no software tag, no provenance string |
| Print resolution | APP0 units 0, density 1 × 1 | the file declares a 1:1 pixel aspect ratio and no physical size at all |
| JFIF thumbnail | APP0 thumbnail 0 × 0 | nothing embedded for file managers to show |
The missing EXIF is the one people notice; the EXIF, GPS and orientation measurement covers that ground separately. The missing resolution is the one that quietly bites: drop this file into a page-layout app and it will be placed at whatever default the app assumes, because the file never states a DPI.
The colour profile is present, and it is a small one. The APP2 segment holds a 456-byte profile: device class mntr, colour space RGB, nine tags — desc, rXYZ, gXYZ, bXYZ, wtpt, rTRC, gTRC, bTRC, cprt. Its description string reads sRGB and its copyright reads Google Inc. 2016 — the same compact sRGB profile the browser attaches to its own output.
Subtracting the entropy-coded image data from the file size leaves everything that is not pixels — the markers, the two quantization tables, the four Huffman tables and the colour profile:
| Output | File | Image data | Everything else |
|---|---|---|---|
| 12-megapixel photo, 0.85 | 3,177,680 B | 3,176,847 B | 833 B (0.03%) |
| Colour photo, 0.85 | 38,078 B | 37,229 B | 849 B (2.23%) |
| Monochrome photo, 0.85 | 35,179 B | 34,356 B | 823 B (2.34%) |
| Flat colour, 0.85 | 2,741 B | 1,983 B | 758 B (27.65%) |
| 16 × 16 image, 0.85 | 910 B | 128 B | 782 B (85.93%) |
The colour photo paid 849 bytes, the monochrome one 823, and the 12-megapixel one 833 — essentially the same number however big the picture is, because the profile and the quantization tables are fixed and only the Huffman tables flex with content. On a 16 × 16 image the wrapper is 85.93% of the file.
Because quality changes nothing but the quantization table, it can only matter where there are coefficients to throw away. I fed the site three images of the same 700 × 476 size at all three settings:
| Content | 0.75 | 0.85 | 0.95 | 0.95 vs 0.75 |
|---|---|---|---|---|
| Flat colour, no detail | 2,741 B | 2,741 B | 2,741 B | 1.00× |
| Real photo | 30,231 B | 38,078 B | 57,617 B | 1.91× |
| Random noise | 190,228 B | 239,219 B | 341,608 B | 1.80× |
The flat image came out the same size at every setting — although not the same file: all three were different byte sequences with distinct SHA-256 hashes. The tables changed; there was simply nothing in the image for finer tables to preserve. A photo and a noisy image roughly doubled between the lowest and highest setting.
It will open anywhere. Baseline sequential, 4:2:0, optimized Huffman, no arithmetic coding, no 12-bit precision, no restart markers, no unusual APP segments. This is the intersection of everything every JPEG reader has supported since the 1990s.
It carries no metadata. No camera data, no location, no date, no orientation flag, no DPI. Anything downstream that expects those will find nothing — see the EXIF measurement for why.
Colour detail is stored at half resolution. That is what 4:2:0 means, and it is right there in the frame header. For photographs it is invisible. For screenshots, scans of documents, or anything with saturated hairlines and thin coloured text, colour edges are stored coarsely than brightness edges.
Black-and-white photos are stored in colour. Three components, always. A monochrome image came out only 7.6% smaller than its colour original because two chroma planes are still being written, even though their Huffman tables reduced to a single symbol each.
The quality number is honest. The 95 on the menu produces quantization tables that a JPEG reference implementation at quality 95 would produce, entry for entry. Whatever you think of canvas JPEG encoding, the dial is not inflated.
No. All 37 files used FFC0 (baseline) and none used FFC2 (progressive). Each had exactly one scan covering all 64 coefficients.
4:2:0, in every file: luma at 2 × 2 sampling, both chroma planes at 1 × 1, giving a 16 × 16-pixel minimum coded unit. A 12-megapixel photo is 47,628 MCUs.
No. There is no FFE1 segment in any output. The EXIF, GPS and orientation measurement covers this in detail.
Nothing I could find in the file structure. Only the two quantization tables change; the frame header, sampling, profile, segment order and component count are identical across all three settings. Speed is unaffected too — the size and speed measurement has those numbers.
A 456-byte sRGB profile in a single APP2 chunk, device class mntr, colour space RGB, nine tags, described as sRGB and credited to Google Inc. 2016.
The live page's own HTML explains it in one line. It calls HeicTo({blob:file,type:'image/jpeg',quality:quality}) — the quality value is read straight off the menu and handed to the decoder, and the type requested is plain image/jpeg. Inside the decoder script the final step is canvas.toBlob(...), and the page's own copy says the quality is "passed directly to the browser's native canvas encoder". Everything above follows from that: the browser's encoder, called with a type and a number, produces baseline 4:2:0 with optimized Huffman tables and the standard quantization tables scaled to the number it was given. The page's HTML also names libheif 1.22.2 as the decoder underneath.
Try it: convert a photo, then open the JPG in a hex editor — the first markers you will see are FFD8 FF E0 for JFIF and FF E2 for the colour profile, and the last two bytes will be FF D9.
First published: 9 October 2026