Guide · measured 19 October 2026
Rotate a JPEG without losing quality
“Lossless rotation” is printed on a great many buttons, and it means two very different things. Usually it means the tool decodes the picture, turns the pixels and writes a new JPEG at a high quality setting — which is not lossless at all, only gentle. Occasionally it means the compressed data itself was rearranged and nothing was decoded, which is exactly lossless. The only way to tell them apart is to measure, so this page measures.
The experiment
One photograph, 1,792 × 896 pixels, saved as a baseline JPEG at quality 90 with 4:2:0 chroma subsampling. Both dimensions are multiples of 16, the block size that matters for this file. It was then turned clockwise by 90° four times — a full revolution, so the final picture should be the starting picture — in two ways:
- Re-encoding. Decode, turn the pixels, encode again at quality 92, four times over: what a canvas-based tool does, using a libjpeg-turbo encoder in place of a browser’s.
- Block transform. The engine behind this site’s lossless JPEG page, four times over: the compressed 8×8 blocks are moved and rearranged, and no pixel is ever decoded.
Each final file was decoded and compared with the decoded original, pixel by pixel:
| File | Size | Largest difference | Mean | PSNR | Pixels changed |
|---|---|---|---|---|---|
| Original (quality 90, 4:2:0) | 331,386 bytes | — | — | — | — |
| Re-encoded four times (quality 92) | 347,425 bytes | 23 | 0.61 | 47.4 dB | 50.1% |
| Block transform four times | 329,622 bytes | 0 | 0 | identical | 0% |
Differences are in 8-bit levels per channel (0–255). Half of all pixels in the re-encoded file moved, by up to 23 levels, and the file grew by 5% because each encode has to describe the noise the previous one introduced. The block-transformed file is the same picture: every pixel identical, and 0.5% smaller only because its Huffman tables were rebuilt for this picture.

Why moving the blocks loses nothing
A JPEG does not store pixels. It stores the picture as 8×8 tiles, each described by 64 numbers — how much of each of 64 standard wave patterns the tile contains — rounded to the precision the quality setting allows. That rounding is where JPEG loses detail, and it happens once, when the file is first written.
A quarter turn or a mirror maps every tile onto another tile position, and maps each wave pattern onto another wave pattern exactly: turning a tile swaps its horizontal and vertical frequencies, and mirroring it flips the sign of every odd one. So the turned picture can be written using the same rounded numbers, moved and sign-flipped, with no new rounding. That is what jpegtran has done on the command line for decades, and what this site does in the browser tab. After writing, the page reads its own output back and checks every one of those numbers against the original before it says “written without loss”.
When it is possible, and when it is not
The catch is the edge. Tiles are grouped into blocks of 8 or 16 pixels (16 on the axes where colour is subsampled, which is most camera JPEGs). If a picture is 4,032 pixels wide, that is exactly 252 blocks of 16, and any turn works. If it is 1,000 pixels wide, the last column of blocks is only partly filled, and a turn that has to move that column to the left edge cannot do it exactly. There are three honest answers:
- Trim the partial strip — at most 15 pixels from one or two edges — and turn the rest without loss. The page shows exactly how many pixels go.
- Keep every pixel and re-encode, which costs one generation. The page labels the file that way.
- Choose a turn that does not move the partial edge. A 90° clockwise turn moves the bottom edge; 90° anticlockwise moves the right edge; 180° and mirrors move both.
Most phone and camera photographs (4032 × 3024, 4000 × 3000, 6000 × 4000) are whole numbers of blocks, so the question rarely comes up. Screenshots and cropped pictures are where it does.
What else to check
- Turn the camera original, not a copy from a messaging app: that copy has already been re-encoded at least once.
- A free angle — straightening by 3° — is never lossless in any format. Every pixel is resampled. Do the quarter turn losslessly first, then straighten once if you must.
- After a turn, the orientation tag must be 1, or a viewer that reads it will turn the picture a second time. The tools here write it as 1 and unlink the stale embedded preview.
- A progressive JPEG is written back as baseline. The picture data is identical; only the order in which a slow connection would paint it changes.
Method
Measured on 19 October 2026. Re-encoding used Pillow 12.3 (libjpeg-turbo) at quality 92 with 4:2:0 subsampling; the block transform used this site’s own engine, the same code that runs in the browser. Pixels were compared after decoding both files with the same decoder. The figures will differ by picture and encoder, but the zero on the last row will not.
Try it on your own file: rotate a JPEG losslessly · or turn any picture on the main tool.


