Reduce JPG file size: where the bytes actually come off
A JPEG file is not a flat sequence of pixels. It is a stack of typed segments: a 2-byte header (SOI), optional application markers (APP0, APP2), one or more quantization tables (DQT), a frame header (SOF0), a set of Huffman tables (DHT), a scan header (SOS), the entropy-coded data — the actual picture — and a 2-byte trailer (EOI). When a compressor reduces the file size, it has exactly one segment it can shrink: the entropy-coded data. The tables are structural metadata, and the question this page answers is how much of each target is overhead, how much is picture, and whether the tables ever change. One 689,299-byte photograph was compressed to eight byte targets from 10 KB to 500 KB, and each output was parsed byte by byte to separate the segments. Then six different images were run at one target to see whether content moves the split.
Three numbers worth knowing before you reduce a JPG
- The quantization tables never change. The DQT segment was 138 bytes — two tables of 69 bytes each — at all eight targets from 10 KB to 500 KB. The compressor adjusts how aggressively the fixed tables are applied, not the tables themselves. What changes is the entropy-coded data: 9,370 bytes at 10 KB, 509,032 at 500 KB.
- At 10 KB, three percent of the file is overhead. Segment overhead (DQT + SOF + DHT + SOS header + APP markers) was 311 bytes at 10 KB, or 3.05% of the 10,210-byte output. At 500 KB the same overhead was 332 bytes, or 0.06%. The one-percent crossover sits between 30 KB and 50 KB on this photograph.
- The Huffman tables grow with quality, not with target. DHT went from 140 bytes at 10 KB (quality 41.41%) to 161 bytes at 500 KB (quality 81.25%) — a 15% increase. Higher quality means more distinct coefficient values survive quantization, which needs a larger Huffman tree. At 60 KB on six different images, DHT ranged from 112 bytes (smooth gradient) to 243 bytes (line chart).
One 689,299-byte JPEG, eight targets, 96 segment measurements, then six content types at one target — 72 more. Measured 7 November 2026.
How to read these numbers: every output file is this site's own compressor driven through its
published interface, one run per target, each in a fresh tab. The output blob was read with
FileReader.readAsArrayBuffer and parsed segment by segment: every 0xFF marker was
walked, each segment's length was read from its two-byte length field, and the entropy-coded data was measured
from the end of the SOS header to the EOI marker, excluding byte-stuffed 0xFF00 pairs and restart
markers. Bytes were also read from the tool's own report() object, and the two agreed on all eight
targets. Dimensions were read two ways (createImageBitmap and the tool's report) and agreed on all
eight. The source file's segments were parsed the same way for comparison. Whatever phrasing brought you here —
reduce jpg file size, shrink a JPEG, compress a JPG — the file structure is the same. If your goal is
specifically to lower the visual quality of an image rather than hit a byte target,
the image quality reducer page measures that trade-off directly.
The JPEG file: eleven segments in the source
The source is a 3000 × 2000 night photograph, 689,299 bytes, already JPEG. Parsing its bytes produces eleven segments: SOI (2 B), APP0 (18 B), two DQT tables (69 B each), SOF0 (19 B), four DHT tables (33 + 183 + 33 + 183 B), SOS header (14 B), entropy-coded data (687,595 B), and EOI (2 B). The tables total 603 bytes — 0.09% of the file. The entropy-coded data is 687,595 bytes — 99.75%. The remaining 79 bytes are markers and the APP0 segment. This is the shape of a JPEG that has never been through this tool: large Huffman tables (432 B total, because the original encoder used standard Huffman trees), no APP2, and the picture occupying almost every byte.
| Segment | Bytes | % of file | What it holds |
|---|---|---|---|
| SOI | 2 | 0.0003% | Start-of-image marker, fixed |
| APP0 | 18 | 0.003% | JFIF header (version, density units, thumbnail) |
| DQT × 2 | 138 | 0.020% | Two 64-entry quantization tables (luma + chroma), one byte per entry |
| SOF0 | 19 | 0.003% | Frame header: dimensions, channels, sampling factors |
| DHT × 4 | 432 | 0.063% | Four Huffman tables (DC + AC, luma + chroma) |
| SOS header | 14 | 0.002% | Scan header: channel assignment, table selectors |
| Entropy data | 687,595 | 99.75% | The actual picture: DCT coefficients, Huffman-coded |
| EOI | 2 | 0.0003% | End-of-image marker, fixed |
| Total | 689,299 | 100% |
Source file: bt-night-3000x2000.jpg, 689,299 bytes. Parsed by walking every 0xFF marker and reading the two-byte length field. The entropy data was measured from the end of the SOS header to the EOI, excluding byte-stuffed 0xFF00 pairs.
Eight targets, one photograph: the segment audit
The photograph went through the compressor at 10, 20, 30, 50, 60, 100, 200 and 500 KB. Each output was parsed the same way as the source. The results are in the table below: total bytes, segment overhead (DQT + SOF + DHT + SOS header), entropy bytes, and the percentage each represents.
| Target | Total bytes | DQT | DHT | SOF | SOS hdr | APP | Table total | Table % | Entropy | Entropy % | Quality |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 10 KB | 10,210 | 138 | 140 | 19 | 14 | 492 | 311 | 3.05% | 9,370 | 91.77% | 41.41% |
| 20 KB | 20,385 | 138 | 140 | 19 | 14 | 492 | 311 | 1.53% | 19,534 | 95.83% | 40.47% |
| 30 KB | 30,716 | 138 | 141 | 19 | 14 | 492 | 312 | 1.02% | 29,827 | 97.11% | 40.47% |
| 50 KB | 50,980 | 138 | 143 | 19 | 14 | 492 | 314 | 0.62% | 50,049 | 98.17% | 40.47% |
| 60 KB | 61,310 | 138 | 144 | 19 | 14 | 492 | 315 | 0.51% | 60,273 | 98.31% | 40.47% |
| 100 KB | 102,087 | 138 | 148 | 19 | 14 | 492 | 319 | 0.31% | 100,868 | 98.81% | 40.47% |
| 200 KB | 202,286 | 138 | 150 | 19 | 14 | 492 | 321 | 0.16% | 200,885 | 99.31% | 58.28% |
| 500 KB | 511,019 | 138 | 161 | 19 | 14 | 492 | 332 | 0.06% | 509,032 | 99.61% | 81.25% |
Eight outputs from the same 689,299-byte photograph. "Table total" is DQT + DHT + SOF + SOS header. "APP" is APP0 (18 B, JFIF header) + APP2 (474 B, ICC colour profile), both injected by the tool. Bytes: report() vs FileReader, agreed on all eight. Dimensions: report() vs createImageBitmap, agreed on all eight.
Read down the DQT column: 138, 138, 138, 138, 138, 138, 138, 138. The quantization tables are identical at every target. The compressor does not shrink the file by making the quantization table coarser — it keeps the same 64-entry, one-byte-per-entry table and applies it harder, which produces fewer non-zero DCT coefficients, which produces a shorter entropy stream. The table is the rulebook; the entropy data is the game, and the game gets shorter.
The DHT column moves: 140, 140, 141, 143, 144, 148, 150, 161. At 10 KB the four Huffman tables total 140 bytes; at 500 KB they total 161. The increase is small in absolute terms — 21 bytes — but it is monotonic and it tells you something: higher quality means more distinct coefficient values survive quantization, and more distinct values need more Huffman codes, which means a bigger table. The source file's DHT was 432 bytes, more than double any output, because the original encoder used full-length standard Huffman tables rather than the optimized ones this tool builds for each image.
The overhead curve: where one percent lives
The table overhead — DQT, SOF, DHT, SOS header, and APP markers combined — was 803 bytes at 10 KB and 824 bytes at 500 KB. That is a 21-byte range across a 50× spread in total file size. The overhead is almost constant; what changes is the denominator. At 10 KB, 803 bytes of overhead in a 10,210-byte file is 7.86%. At 500 KB, 824 bytes in 511,019 is 0.16%. The one-percent line — where the overhead is exactly one percent of the file — falls between 30 KB and 50 KB, at roughly 31,200 bytes if you interpolate the overhead as 312 bytes. Below that line, the tables and markers are a measurable tax; above it, they are rounding.
The APP2 segment is 474 bytes in every output and absent from the source. It is an ICC colour profile that the tool injects to preserve colour fidelity across devices. It is the single largest piece of overhead, and it is constant: it does not grow with quality, shrink with target, or change with content. On a 10 KB file it is 4.6% of the output; on a 500 KB file it is 0.09%. If you wanted to strip every non-essential byte, removing the APP2 would save 474 bytes — but the colour would shift, and that is a content decision the tool does not make for you.
Six content types at one target: does the split move?
The first experiment held the image constant and varied the target. The second holds the target constant — 60 KB — and varies the image. Six files of different content types were run through the tool: a night photograph, a product photo, a line chart, a vector graphic, a text screenshot, and a smooth gradient.
| Image | Source bytes | Output bytes | Dims | Quality | DQT | DHT | Table total | Table % | Entropy | Entropy % |
|---|---|---|---|---|---|---|---|---|---|---|
| Night photo (3000×2000) | 689,299 | 61,310 | 1831×1221 | 40.47% | 138 | 144 | 315 | 0.51% | 60,273 | 98.31% |
| Product photo (2000×2000) | 117,632 | 59,923 | 2000×2000 | 84.06% | 138 | 178 | 349 | 0.58% | 58,933 | 98.35% |
| Line chart (1600×900) | 28,605 | 59,762 | 1600×900 | 94.38% | 138 | 243 | 414 | 0.69% | 58,592 | 98.04% |
| Vector graphic (4000×4000) | 84,394 | 61,275 | 2234×2234 | 40.47% | 138 | 171 | 342 | 0.56% | 60,282 | 98.38% |
| Text screenshot (1200×1600) | 233,773 | 61,373 | 534×712 | 40.47% | 138 | 142 | 313 | 0.51% | 60,325 | 98.29% |
| Smooth gradient (2400×1600) | 2,537,877 | 61,140 | 2400×1600 | 70.47% | 138 | 112 | 283 | 0.46% | 60,268 | 98.57% |
Six images compressed to 60 KB JPG each. Source types: JPG (night photo, product), PNG (chart, graphic, text, gradient). DQT is 138 bytes in every case. DHT ranges from 112 (gradient) to 243 (chart). Table overhead is under 1% of the output in every case.
The DQT column is 138 bytes six times. The quantization tables are not content-dependent either: the encoder always writes two 64-entry tables, one for luma and one for chroma, and the entry count is fixed by the JPEG specification. What is content-dependent is the Huffman table. The smooth gradient produced a DHT of 112 bytes — the smallest — because a gradient has very few distinct coefficient values after quantization, and a small alphabet needs a small Huffman tree. The line chart produced 243 bytes — the largest — because sharp edges generate high-frequency coefficients across many frequency bands, which means many distinct values, which means many codes.
The quality column tells the rest of the story. The product photo came back at 84.06% quality with all 4,000,000 pixels intact, because a clean product shot on a plain background has little to quantize. The night photograph and the text screenshot both hit the 40.47% quality floor and were scaled down — to 1831 × 1221 and 534 × 712 respectively — because their content is dense and the tool ran out of quality room before it ran out of byte budget. Same target, same tool, six different outcomes, and the segment audit shows exactly where the bytes went in each one.
What the DQT invariance means
The quantization table is a 64-entry matrix that scales each of the 64 DCT frequency coefficients. A higher value in the table means that frequency is quantized more aggressively — rounded to a coarser step — which produces more zeros in the coefficient stream, which produces a shorter entropy-coded section. The table itself is one byte per entry, 64 entries per table, two tables (luma and chroma): 128 bytes of payload plus two bytes of overhead = 138 bytes, and that number is fixed by the JPEG specification, not by the encoder.
What the compressor does change is the quality setting, which scales the table values before applying them. At quality 40.47% the table values are large; at quality 81.25% they are smaller. But the scaled values are applied at encode time and are not stored in the file — the file stores the unscaled table, and the decoder applies the same scale factor when reading it back. This is why the DQT segment is the same 138 bytes at 10 KB and 500 KB: the table's representation in the file does not change, even though the table's effect on the picture does. If you want to see the effect, the JPEG size reducer page measures it channel by channel — the Y, Cb and Cr errors that the quantization step produces at each target.
The Huffman table: the only segment that moves
Of the four structural segments — DQT, SOF, DHT, SOS header — only the DHT changes size across targets or content. The DHT holds the Huffman code tables that map coefficient values to variable-length bit strings. When more distinct coefficient values survive quantization (high quality, complex content), the Huffman tree needs more branches, which means more bytes to describe it. When fewer values survive (low quality, smooth content), the tree is smaller.
The range across both experiments: 112 bytes (smooth gradient at 60 KB) to 243 bytes (line chart at 60 KB) on the content side, and 140 bytes (10 KB target) to 161 bytes (500 KB target) on the target side. The content side varies more than the target side — 2.17× vs 1.15× — because content complexity has a larger effect on Huffman table size than quality does. A smooth gradient at any quality will always have a small Huffman tree, because the DCT of a smooth ramp produces very few non-zero coefficients. A line chart at any quality will always have a larger one, because edges produce energy across the frequency spectrum.
Which JPEG segment can a compressor actually shrink?
The practical consequence for anyone trying to shrink a JPEG's byte count is that the compressor has one lever — the entropy-coded data — and it pulls it by adjusting the quality setting, which scales the quantization table's effect without changing the table itself. The table overhead is fixed at roughly 300–350 bytes plus a 492-byte APP2 segment, which means on files below 30 KB the overhead is a measurable fraction of the output, and on files above 50 KB it is negligible.
The second consequence is that the tool always injects a 474-byte ICC colour profile (APP2) that the source file may or may not have had. On the source photograph here, the original had no APP2 — 0 bytes of colour profile — and every output has 474. That is 4.6% of a 10 KB file and 0.09% of a 500 KB file. If the target is very tight and the colour profile is not needed, stripping it would save nearly half a kilobyte. The tool does not offer that option, and the colour shift from removing it is a content decision, not an encoder setting.
How to shrink a JPEG with the segment map in mind
- Pick a target, not a quality. The byte target is what your upload form enforces, and the tool hits it by adjusting quality — the table structure does not change. Guessing a quality percentage is guessing how many bytes the entropy section will be, which is harder than just saying how many bytes you want.
- Below 30 KB, expect overhead to matter. On this photograph, the segment overhead was 3.05% at 10 KB and 1.02% at 30 KB. At very small targets the 492-byte APP2 and 138-byte DQT are a real share of the budget, and the entropy data gets whatever is left.
- The Huffman table tracks content, not target. A smooth image will have a smaller DHT (112 bytes on the gradient) and a chart will have a larger one (243 bytes). The difference is 131 bytes, which is negligible at 60 KB but would be 1.3% of a 10 KB file.
- Run once and stop. The tool is deterministic — the second-pass page measured a second encode at the same target returning the identical file. Running it again does not remove more bytes from the entropy section, because the Huffman-coded stream is already at the target.
- If the output must stay JPG, the table structure is immutable. You cannot remove the DQT or DHT without making the file undecodable. The only byte-removal mechanism available to a JPEG re-encoder is shortening the entropy stream, which means coarser quantization, which means lower quality or fewer pixels. The 200 KB page measures exactly where that switch happens.
Questions this page gets
Where do the bytes come off when a JPEG is compressed to a smaller target?
From the entropy-coded data. On a 689,299-byte JPEG compressed to eight targets from 10 KB to 500 KB, the DQT stayed at 138 bytes, SOF at 19, SOS header at 14, and DHT moved from 140 to 161. The entropy-coded data went from 9,370 bytes (91.77% of the 10 KB output) to 509,032 bytes (99.61% of the 500 KB output). Every byte removed between one target and the next came from that segment.
Do the JPEG quantization tables change when you compress to a smaller file?
No. The DQT segment was 138 bytes — two tables of 69 bytes each — at all eight targets from 10 KB to 500 KB. The compressor adjusts the quality setting, which scales the table values at encode time, but the table stored in the file does not change: 64 one-byte entries per table, two tables, always.
How much of a small JPEG is overhead?
At 10 KB the overhead was 311 bytes, or 3.05% of the output. At 500 KB it was 332 bytes, or 0.06%. The one-percent crossover sits between 30 KB and 50 KB. The overhead includes DQT (138 B), DHT (140–161 B), SOF (19 B), SOS header (14 B), and APP markers (492 B, of which 474 is the ICC colour profile).
Does the segment split change with different content?
The DQT is always 138 bytes. The DHT varies: at 60 KB, six images produced DHT sizes from 112 bytes (smooth gradient) to 243 bytes (line chart), a 2.17× spread. Table overhead ranged from 0.46% to 0.69%, and the entropy share from 98.04% to 98.57%. The split stays close because the table is tiny relative to 60 KB.
Is the JPG uploaded when I reduce its file size?
No. Decoding, re-encoding and the byte-target search all run inside the browser tab. Every measurement on this page came from the same in-browser compressor you can use yourself, and no request carrying image data leaves the machine.
Can I reduce a JPG to an exact file size like 30 KB?
You can set an exact target and the tool returns a file at or under it — the 30 KB run returned 30,716 bytes. The segment audit shows 312 bytes of overhead (DQT 138, DHT 141, SOF 19, SOS 14) and 29,827 bytes of entropy-coded data at that size. Quality was 40.47% and dimensions were 1230 × 820, because the 6-megapixel source could not fit 30 KB at full frame above the quality floor.
Where the numbers come from. All figures measured on 7 November 2026 with Chrome
157.0.8103.38 (headless, driven over the DevTools protocol) on Windows 11, using this site's compressor
through its published interface (window.exactSize). Experiment one: one 3000 × 2000 photograph,
689,299 bytes, eight targets (10, 20, 30, 50, 60, 100, 200, 500 KB), one run per target in a fresh tab.
Each output blob was read with FileReader.readAsArrayBuffer and parsed byte by byte: every
0xFF marker was walked, segment lengths were read from two-byte length fields, and entropy data
was measured from the SOS header end to the EOI marker, excluding 0xFF00 stuff bytes and RSTn
markers. Bytes were cross-checked against the tool's own report().resultBytes on all eight
targets; dimensions were cross-checked against createImageBitmap on all eight. Experiment two:
six images of different content types (night photograph, product photo, line chart, vector graphic, text
screenshot, smooth gradient) each compressed to 60 KB JPG, each output parsed the same way. The five
whole-frame byte values (20,385 / 61,310 / 102,087 / 202,286 / 511,019) are byte-identical to this site's
runs on the same photograph on 30 October, 2 November, 4 November, 5 November and 6 November — six sessions,
same bytes, which is the cheapest proof that the encoder is deterministic. One photograph only: the segment
structure is a property of the JPEG specification, but the Huffman table sizes and the overhead percentages
are properties of this image's content and this tool's encoder. A different image would produce different
DHT sizes (the content experiment shows how much) but the same DQT size (138 B, fixed by spec).
Read these alongside: the JPEG size reducer splits the same damage by colour channel (Y, Cb, Cr); the 200 KB page measures where quality stops and pixels start; the second-pass page shows that a second encode at the same target returns the identical file; the spatial distribution page cuts the photograph into 40 tiles and maps where the error lands; the image quality reducer measures what happens when you trade bytes for visible quality directly — how much worse does the picture get per kilobyte removed; the KB ladder puts six single-pass budgets in one table; and the PNG size reducer removes bytes without touching a pixel — a different mechanism entirely.