Start with the problem: raw pixels are enormous
An uncompressed image stores three bytes (red, green, blue) per pixel. A 12 MP photo is therefore 36 MB raw. Yet the JPG your camera writes is ~3 MB and a well-compressed web copy is ~400 KB — a 90× reduction. Compression finds and removes two kinds of waste: redundancy (patterns that can be described more compactly) and irrelevance (detail your eyes can't see anyway).
Step 1: exploit how weak your color vision is
The encoder first converts RGB into one brightness channel (luma) and two color channels (chroma). Reason: human vision is sharp for brightness and blurry for color. So JPG keeps luma at full resolution and stores chroma at half resolution in each direction — an instant 50% saving before compression even begins, and you have never noticed it in any photo you've ever seen.
Step 2: the DCT — turning pixels into frequencies
The image is cut into 8×8 pixel blocks, and each block passes through the Discrete Cosine Transform, which re-describes those 64 pixels as a recipe of 64 wave patterns: one number for the block's average, then coefficients for progressively finer detail. Nothing is lost yet — it's the same information in a form where "smooth stuff" and "fine detail" sit in separate slots.
Why bother? Because in real photos, most blocks are mostly smooth: the fine-detail slots hold values near zero. The transform concentrates the image's energy into a few numbers — setting up the kill shot.
Step 3: quantization — where quality is decided
Now the lossy part. Each frequency coefficient is divided by a step size and rounded to the nearest integer. Fine-detail coefficients get divided by large steps, so most round to exactly zero. This rounding is the entire quality dial: quality 85 uses gentle steps (tiny, invisible rounding), quality 40 uses brutal ones (visible blocky artifacts). The discarded precision is gone forever — this is the "loss" in lossy.
Step 4: entropy coding — free compression on top
After quantization, blocks are mostly runs of zeros with a few significant numbers. Classic lossless tricks — run-length encoding and Huffman coding (frequent values get short codes) — pack this sparse data into very few bytes. This stage loses nothing; it's pure packing.
| Stage | Lossy? | Contribution |
|---|---|---|
| Chroma subsampling | Yes (imperceptible) | ~2× before anything else |
| DCT | No — reorganizes only | Concentrates energy for step 3 |
| Quantization | Yes — the quality dial | The bulk of the savings |
| Entropy coding | No | Final ~2–3× packing |
Modern formats: same skeleton, sharper tools
WebP, HEIC and AVIF keep the pipeline but upgrade each stage with video-codec technology: flexible block sizes instead of fixed 8×8, prediction (encoding only the difference from a guessed block), and arithmetic coding. Each generation buys roughly 25–50% over its predecessor — the details of when to use which are in Best image format for websites.