Use the 90° buttons for quarter turns — they move every pixel to a new cell and recompute nothing, which we verified pixel by pixel: 0 of 12,000,000 pixels changed on a 3000×4000 photo rotated 90° in a browser canvas. A 180° turn is two of those, also exact. The angle slider is a different operation: any angle that is not a multiple of 90° resamples the picture and grows the frame around it — the arithmetic is on our 3D rotate guide, and this page will not repeat it.
What the tool pages leave out: an exact turn still hands you back a newly encoded file. The dimensions swap (3000×4000 becomes 4000×3000), the bytes change, and if you rotate the same file through four export-and-reload cycles, 32.4% of its pixels sit more than 2 levels away from where they started — the first re-encode at quality 90 alone accounts for 28.3 points of that. Nothing about those numbers is about the rotation. They are about the re-save.
Every online rotator we opened for this page offers the same three controls in different clothes: a quarter turn left, a quarter turn right (or one 180° button), and an angle slider. The quarter turns are a permutation — the pixel at position (x, y) lands at a new cell, and its value is copied, never interpolated. The 180° turn is a quarter turn applied twice. The slider is the odd one out: an arbitrary angle cannot land on the pixel grid, so the tool has to invent values between cells, and the turned picture no longer fits its old rectangle.
One photo, three controls. Two of them only copy pixels; the slider is the one that redraws the picture.
Here is the part that matters for anyone rotating a picture they care about. We loaded the 3000×4000 test photo into a browser canvas, applied a single 90° clockwise quarter turn, and exported the result two ways. Then we compared the export, pixel by pixel, against the source after the same exact permutation applied locally. The comparison covered all 12,000,000 pixels with zero tolerance.
| Export after one 90° turn | Pixels differing from the exact permutation | Dimensions | File size |
|---|---|---|---|
| PNG (lossless) | 0 of 12,000,000 (0%) | 4000×3000 | 14,307,108 bytes |
| JPG, quality 90 | 0 before export — re-encode shifts 28.3% by more than 2 levels (see next section) | 4000×3000 | 2,129,585 bytes |
| Source file, for scale | — | 3000×4000 | 3,019,770 bytes |
Protocol: source decoded in Chrome, drawn to a canvas with a 90° clockwise transform (ctx.rotate(), whose positive angle is clockwise per the platform documentation), exported via canvas.toBlob(). The PNG was compared against a locally computed permutation of the decoded source with a per-channel tolerance of 0. The JPG row’s 28.3% is the re-encode figure measured in the next section, not a rotation effect.
Four quarter turns in a single pass — a full 360° — produced the same result at the pixel level: 0 of 12,000,000 pixels differ from the original photo. The exported PNG weighs 14,790,138 bytes. Same pixels; different file. That gap is the quiet cost of every online rotate: even a mathematically exact turn is delivered as a fresh encode, and the fresh encode is where your file changes. If you need the original bytes preserved bit for bit, no browser tool will do it — the lossless route is below.
“Rotate left” and “rotate right” are not a universal language, and that is where most wrong-way rotations happen. What we could verify, from the tools and official documentation themselves:
Phone photo apps are where the vocabulary drifts, because the single rotate button on a phone is a fixed direction and the label says nothing about which. The safe habit: rotate once, look at the preview, and if the picture faces the wrong way press again twice rather than hunting for a second button. A quarter turn is exact and free; three wrong quarter turns cost you nothing but a moment.
The folk warning says every save of a JPG shaves off more quality. Our numbers say something more precise, and more useful. We took the same photo and ran four generations of the same cycle a browser tool performs: rotate 90°, export at quality 90, reload that export, rotate again. After each generation we compared the decoded file against the exact rotated source, counting pixels that moved by more than 2 levels in any colour channel.
| Generation (each = one rotate + one quality-90 export) | Pixels beyond ±2 levels | Share of 12,000,000 | JPG size |
|---|---|---|---|
| 1st export | 3,399,841 | 28.332% | 2,129,585 bytes |
| 2nd export | 3,769,291 | 31.411% | 2,113,263 bytes |
| 3rd export | 3,801,651 | 31.680% | 2,133,271 bytes |
| 4th export | 3,892,558 | 32.438% | 2,099,579 bytes |
Protocol: source photo decoded in Chrome; each generation drawn with a 90° clockwise transform, exported as JPG at quality 90, reloaded from those bytes. Comparison against the locally computed exact rotated source, per-channel tolerance 2, all 12,000,000 pixels counted. A control run with no rotation at all produced the same pattern, which is how we know the rotation is not the cause.
The largest jump is the first one. After that, each further generation adds a point or two, and the fourth export is smaller than the first — the drift does not simply grow forever. A third-party video test that measured repeated re-saves with a perceptual quality score reached the same shape of conclusion: the first save is where the cost lands, and later saves at the same settings barely move the score. Rotate once from your original, at the best quality the tool offers, and the damage is bounded.
If the re-encode is the cost, the next question is which quality setting the tool baked in. We re-saved the same photo at seven quality settings without rotating it, and counted pixels beyond ±2 levels each time.
| JPG quality setting | Share of pixels beyond ±2 levels | File size |
|---|---|---|
| 75 | 34.882% | 1,269,979 bytes |
| 80 | 33.309% | 1,453,932 bytes |
| 85 | 31.062% | 1,666,969 bytes |
| 90 | 28.559% | 2,191,027 bytes |
| 92 | 25.598% | 2,443,475 bytes |
| 95 | 11.299% | 2,993,036 bytes |
| 98 | 12.254% | 3,884,154 bytes |
Measured with a local encoder on the same decoded source photo, one re-save per row, no rotation. The browser’s quality-90 export measured 28.332% drifted pixels against the same tolerance — the encoders differ slightly, the picture is the same: the drift is the encode, not the turn.
Two things fall out of that table. Quality 90 is not the top of the scale — it sits where the curve still bends, and 95 holds the drift at two-fifths of quality 90’s for 37% more bytes. And the tool, not you, picks this number: most online rotators offer a single fixed export quality. A tool that lets you raise the quality, or export PNG, hands the decision back; a tool that does not has decided for you.
The turn is exact; the file is new. PNG keeps every pixel and pays for it in bytes; quality-90 JPG is smaller than the source but re-encodes the picture.
An animated GIF is many frames in one file. A browser canvas holds one frame at a time. We built a 6-frame test GIF (900×1200, 24,462 bytes), rotated it 90° through a canvas, and exported. The result is a perfectly rotated still: 1200×900, 28,096 bytes as PNG or 24,512 bytes as JPG — and exactly 1 frame, counted from the file itself. The other five frames did not survive the trip.
That is not a bug in one tool; it is the shape of the problem. Scripting libraries hit the same wall — the standard first-frame-only behaviour is such a common trap that tutorials for flipping or rotating GIFs open by warning about it. Some server-side batch tools list GIF rotation as a separate path, which is the honest place for the problem to live, because rotating six frames means rotating each one. What no tool can do is keep the animation inside a canvas that only ever holds one frame. If your file moves, check the export before you download it.
Tool pages list formats like a menu. We tested which ones a real browser decodes, by trying to load each sample in the page and reading its dimensions back.
| Format | Decoded in Chrome? | Result |
|---|---|---|
| JPEG | Yes | 3000×4000 loaded |
| PNG | Yes | 1200×1600 loaded |
| WebP | Yes | 1200×1600 loaded |
| GIF | Yes (first frame) | 900×1200 loaded |
| BMP | Yes | 1200×1600 loaded |
| AVIF | Yes | 1200×1600 loaded |
| ICO | Yes | 256×256 loaded |
| TIFF | No | decode failed |
Each sample was created from the same photo and offered to the browser as a blob. “Decoded” means the image element fired its load event with non-zero dimensions. A tool that lists TIFF is converting on a server, not in your browser — that is a different privacy story and a different queue.
A quarter turn of a JPG does not have to touch the picture at all. Because a JPEG stores the image as a grid of frequency blocks, a 90° or 180° turn (and a mirror) can be done by rearranging whole blocks inside the compressed data. The standard tool for this is jpegtran, shipped with the reference JPEG library; its manual describes the rotate and flip transforms as lossless, alongside a lossless crop, and a Python binding for the same code documents the same split: every transform except scaling is lossless. Desktop rotators built on it — right-click batch rotators, photo library plugins — advertise exactly this, and one tool’s tutorial material claims its online quarter turns re-encode nothing. Those are their claims and their documentation; we did not run them, and this page does not count them as our measurements.
The catch is the boundary. Lossless rotation covers the quarter turns and flips only, because those are the operations that map the 8×8 block grid onto itself; anything else needs pixels decoded, and decoded means re-encoded on the way out. A browser works on decoded pixels by definition. So the honest split is: quarter turn of a JPG and you want the original file untouched → a lossless tool; anything else, or a PNG (already lossless, so re-export costs nothing but size) → a browser tool is fine.
The whole test ran on one Windows machine with a desktop Chrome and one script, plus local inspection of the exported files. Three inputs: a 3000×4000 JPG (3,019,770 bytes), a 6-frame 900×1200 GIF (24,462 bytes), and one sample per format (JPEG, PNG, WebP, GIF, BMP, AVIF, ICO, TIFF). Every number in the tables above came out of those files: dimensions decoded from the outputs, byte sizes read from disk, pixel counts from a full-image comparison with a stated tolerance, frame counts read from the file headers.
What we did not test, and will not claim: the quality of any third-party tool’s rotation, since only the tools’ own pages describe them; the lossless route itself — we did not run a block-level JPEG tool, so its speed and exact compatibility with unusual JPEG variants (progressive, 12-bit) are untested here; other browsers and phones, whose decoders may differ on AVIF and ICO; and any angle that is not 0, 90, 180 or 270 degrees, whose geometry lives on our 3D rotate guide. The drift percentages describe this photo at these settings; the direction (first encode costs most, later encodes add a little, quality setting dominates) is the transferable part, the exact percentages are not.
Open the file in a browser-based rotator, press the 90° button for a sideways photo or the 180° button for an upside-down one, and download. The turn itself is exact — we measured 0 of 12,000,000 pixels changed — so the only decisions that matter are which direction you pressed and what format you export. If the photo came off a phone and only looks sideways, rotate it and then check it in a different viewer; our flip guide explains why that case needs one extra step.
Any tool that opens the image straight into the page rotates it without uploading it — you can confirm that by watching the network tab while you rotate, or simply by noticing the tool still works with your Wi-Fi off. The trade-off is coverage: a browser decodes what it decodes (JPEG, PNG, WebP, GIF, BMP, AVIF and ICO all loaded for us; TIFF failed), and it holds one frame of a GIF at a time. Server-side tools accept more formats and keep GIF animation, because the file leaves your machine first.
One quarter turn in either direction. The dimensions swap — our 3000×4000 portrait came back 4000×3000 — and every pixel survives the move, so the picture keeps its full resolution. Do not reach for the angle slider for this job: it resamples and grows the frame, which is the wrong cost for a simple orientation change. If what you actually want is a re-framed landscape version of a portrait photo instead of a rotated one, that is a different operation, covered in our portrait-to-landscape guide.
The phone’s own photo app has a rotate button inside its edit mode, and it performs the same exact quarter turn a browser tool does. Press it until the picture faces the way you want; wrong presses cost nothing, because quarter turns never resample. When the picture only looks sideways in some apps and right in others, that is the orientation-tag case, and it is answered in full on our flip guide — the short version is that the pixels were never turned, and rotating once in a tool that bakes the tag fixes it everywhere.
Not in a browser, and now you know why: a canvas holds one frame, so the export we measured came back with exactly 1 frame from a 6-frame source — a perfect still, a dead animation. The tools that keep GIFs moving are the ones that receive the whole file and process every frame, and they say so on their own pages. If your file moves, either use one of those or accept a still; there is no browser-side way to keep the frames.