Compress JPEG

Shrink JPG photos with the MozJPEG encoder — Mozilla's JPEG encoder built on libjpeg-turbo, self-hosted by this site and running entirely in your browser. Pick a quality or a target size, and every row shows the percent change on your file, measured from real bytes.

Compress JPEG files with MozJPEG

Drop JPG files (other formats convert to JPEG, disclosed per file), pick quality or a target size, and download verified JPEGs with the measured savings.

or drag and drop images here

Up to 40 files · 100 MB each · 300 MB per selection

Compression mode

    Guide: Quality parameters and compress-to-target explains the quality scale this slider feeds, where the lane's default comes from, and how the target-size search measures its way to a limit, with the planner's own worked examples. The other guides cover detection, the pipeline, metadata and verification.

    This page or the Image Compressor — which one?

    Both pages re-encode your file inside your browser and both report measured bytes; the difference is the encoder. The Image Compressor hands the pixels to the browser's own canvas encoder and can write WebP as well as JPEG. This page hands them to MozJPEG — Mozilla's JPEG encoder built on libjpeg-turbo — compiled from pinned upstream source and served by this site. The lane's record in the engine says what it is for: the target is JPEG and nothing else, its default quality is 75 on the slider's scale, progressive scan order is on by default, and the target-size search is available. The engine's note names the extra work — trellis quantization, optimized Huffman tables, progressive scan scripts, work the canvas encoder does not do — and keeps its claim deliberately narrow: the same decodable JPEG, measurably fewer bytes at the same quality setting. Measurably is the operative word. This page predicts nothing; the row measures.

    So a JPG you want smaller as a JPG belongs here. A WebP you want to stay WebP, or a PNG whose transparency should survive, belongs on the Image Compressor, because the engine's capability table says JPEG cannot hold transparency and WebP can.

    What the detector sees first

    Nothing here trusts a filename. The leading bytes of every dropped file are read and matched against the signatures the detector knows, and for this lane the one that matters is FF D8 FF: an illustrative header built at build time from those three bytes and a JFIF marker comes back from the detector as JPEG. The extension is then compared with that verdict. Three invented names, run through the same check:

    • portrait.jfif with JPEG bytes: claimed JPEG, actual JPEG, mismatch false. The .jfif extension is read as a JPEG claim, so the row shows no notice; the download is renamed portrait.jpg.
    • receipt.png with JPEG bytes: claimed PNG, actual JPEG, mismatch true. The row says the name claims PNG but the bytes are JPEG; the file is compressed as the JPEG it is, and the download is named receipt.jpg.
    • poster.jpg with PNG bytes: claimed JPEG, actual PNG, mismatch true. This one is not a JPEG at all, so it takes the conversion path described below — flattened, then encoded — and the row says so.

    Unlike the Image Compressor, this lane is built to accept every format the site can decode, which is why the drop zone lists PNG, WebP, GIF, BMP, HEIC and AVIF beside JPG — and why the plan has a row for each of them further down.

    What the plan decides for a JPEG

    Before any encode, the planning module is asked for a plan from the detected format to this lane's target. For JPEG in and JPEG out, computed at build time: output type image/jpeg, flatten false, first-frame caveat false, and a background of #ffffff recorded but never used, because a JPEG has no transparency to composite and no frames to drop. The plan also carries a quality field, and here is a detail worth stating precisely: that field is the capability table's default for the browser's canvas JPEG encoder — 92 on the slider's scale — and this lane does not use it. The MozJPEG lane reads its quality from its own record, 0.75 on the engine's 0–1 scale, which the bridge function turns into 75 for the encoder; the slider overrides it, and the row records the number actually passed. Two defaults, two encoders, stated rather than recommended.

    The output name keeps your stem and swaps in the canonical .jpg; when that would reproduce the original name, the suffix -compressed is inserted so the download cannot shadow the file it came from. Computed by the same function the tool calls, on invented names: receipt.jpg becomes receipt-compressed.jpg; Scan.JPG becomes Scan-compressed.jpg, because the comparison ignores case; portrait.jfif becomes portrait.jpg and banner.webp becomes banner.jpg with no suffix, because the extension already differs; a PNG named poster.png becomes poster.jpg.

    Other inputs are converted first — and the row says so

    Drop anything other than a JPEG and the planner's answer changes. One plan per accepted source, each computed at build time against this lane's target:

    Source Flatten onto background First-frame caveat Output type
    PNG true false image/jpeg
    WebP true true image/jpeg
    GIF true true image/jpeg
    BMP true false image/jpeg
    HEIC true false image/jpeg
    AVIF true true image/jpeg

    Every non-JPEG source is flagged for flattening, because the capability table says JPEG cannot hold transparency and each of those containers can — for a fully opaque photo the composite changes nothing, but the plan still asks for a background and the colour control above supplies it (default #ffffff). The first-frame flag is set for WebP, GIF, AVIF, the containers that can be animated, and the row shows the notice only when the detector actually found animation: image descriptors counted for GIF, the VP8X flag and ANIM chunks read for WebP, the avis brand read from the ftyp box for AVIF. HEIC and AVIF sources arrive through decoders this page does not own: HEIC through the self-hosted libheif module described on HEIC to JPG, and AVIF through the browser itself — current Chrome and Edge, Firefox 93+, and Safari 16.4+ decode AVIF natively — with a plain-language notice when a browser cannot. The directional pages ( PNG to JPG, WebP to JPG, AVIF to JPG) and the flattening guide cover those plans in full; on this page the point is only that the conversion is disclosed on the row and the output is named for what it became.

    The encoder, in the engine's terms

    The licensed facts, as the methodology page states them. MozJPEG is Mozilla's JPEG encoder built on libjpeg-turbo — this site includes the libjpeg-turbo software, covered by the IJG, Modified (3-clause) BSD, and zlib licenses. The hardened browser binding is derived from Google's Squoosh/jSquash wrapper under Apache-2.0. License texts ship alongside the module at /vendor/mozjpeg/LICENSE.txt and /vendor/mozjpeg/LICENSE-codec.txt. The codec is compiled from pinned upstream source at an immutable commit, served from this site's own origin as a separately replaceable file, fetched lazily only when a file is actually dropped, and run in a bounded worker entirely in your browser; its exact size and SHA-256 are pinned by automated tests, so served bytes cannot drift silently.

    What it does with your pixels: the browser decodes the file with createImageBitmap and imageOrientation: 'from-image', the bitmap is drawn to a canvas, the canvas's raw pixel data is read out, and that array — not your original file — is what MozJPEG encodes at the quality and scan order you chose. The output is a standard JPEG; the engine's note calls it the same decodable JPEG, and the verification step below treats it as exactly that.

    Progressive or baseline. A JPEG's scan order is a property of the encoder's output, not a different format. The lane record turns progressive order on by default and describes it in two phrases: smaller files and progressive rendering. Untick the box and the encoder writes baseline order instead. Either way the row's note records which was used beside the quality, in the form "MozJPEG quality 75, progressive scan" at the defaults — so a file you cannot account for can be traced to its settings. This page does not claim which order any particular reader prefers; the toggle exists so the choice is yours.

    The quality number, on this lane

    The slider shows an integer; the engine stores a number between 0 and 1; one function bridges them — clamp into the engine's range, multiply by 100, round — onto 0–100. The lane's default of 0.75 becomes 75; the search range's ceiling and floor, asked of the planner itself at build time, become 95 and 5, which is why the slider stops where it does. The number is an instruction to MozJPEG and nothing more: not a fraction of the file, not a share of its detail, not a score the result will earn. Nor is it the same instruction the canvas encoder receives at the same number on the Image Compressor, which is why the two lanes carry two defaults. This page therefore recommends no setting; the guide linked above works the scale through in detail.

    Target size: what the row will say

    Switch to target mode, type a size in KB, and the planner takes over. The field is read in binary kilobytes — 1,024 bytes each, the unit the row displays — and the planner's first instruction, with no encodes made, is always the same: encode at the ceiling, quality 95. From then on it sees only the attempts so far, each a quality and the bytes it produced, and brackets between the highest quality known to fit and the lowest known to overshoot. The lane record declares the search available, and the planner's probes are bounded — at most eight, as the methodology page states.

    To show the lane deciding, this page drives the real planner against an invented size curve, one whose byte count rises with the quality parameter by a formula chosen for the demonstration. The byte counts are arithmetic on made-up numbers and measure no image; only the sequence of qualities and the verdicts are real, because the real planner chose them. Target: 150 KB, which is 153,600 bytes.

    1. Encode 1: MozJPEG quality 95 → 698,162 bytes (681.8 KB) — over the target
    2. Encode 2: MozJPEG quality 48 → 279,160 bytes (272.6 KB) — over the target
    3. Encode 3: MozJPEG quality 24 → 130,765 bytes (127.7 KB) — fits
    4. Encode 4: MozJPEG quality 36 → 198,687 bytes (194.0 KB) — over the target
    5. Encode 5: MozJPEG quality 30 → 162,729 bytes (158.9 KB) — over the target
    6. Encode 6: MozJPEG quality 27 → 146,033 bytes (142.6 KB) — fits

    Verdict after 6 encodes: done, with the attempt at MozJPEG quality 27 and 146,033 bytes — the planner returns the largest attempt that fit, which in this trace is also the last one made. The row carries the client's own wording, with sizes written the way the row writes them: "Target 150.0 KB: reached 142.6 KB at MozJPEG quality 27 in 6 encodes." Against an invented original of 980,000 bytes the percent on that row would read "85% smaller", computed from those two byte counts and nothing else.

    The same invented curve asked for 40 KB (40,960 bytes) never fits: the planner halves toward its floor through 5 encodes, the lowest at MozJPEG quality 6, and returns the verdict unreachable with the smallest attempt it made, 60,032 bytes at quality 6: the bracket left between quality 6 and the floor of 5 was too narrow to split, so the planner returned unreachable without ever probing the floor itself. The row then says, in the client's words, that the target is not reachable for this image even at minimum quality, names the smallest achievable size, and offers that file below. It does not round the verdict up to success. The lever that remains is pixel dimensions, which this page never changes — the Image Resizer does.

    What a re-encode strips

    Because MozJPEG receives the canvas's pixel array rather than your file, nothing that lived in the file's own segments survives. Canvas re-encoding strips all embedded metadata: EXIF (including GPS location, camera model and timestamps), colour-profile tags, and text chunks. That is both a feature and a caveat — a compressed photo cannot leak where it was taken, and equally, copyright fields and ICC profiles are gone, so keep your originals. Orientation is applied during decoding, before the metadata is discarded, so a sideways phone photo comes out upright even though the orientation tag itself does not survive. A re-encode is also another lossy pass over pixels that have already been through at least one; the tool measures what it produced in bytes, not what the pass removed, and reports growth as plainly as shrinkage when the measured bytes go the wrong way. The metadata guide lists what goes and what stays.

    How the output is verified

    A download button means two checks have already passed and a third comparison has been made. The produced bytes are sniffed with the same signature detector applied to inputs, so an encoder that produced anything other than a JPEG is reported as an error rather than handed to you as a mislabeled file. The blob is re-decoded and its pixel width and height compared with the canvas that was encoded; a mismatch is an error, never a silent pass. And the output's true byte count is compared with the input's: when the result is not smaller, the row adds a note that the file was already well optimized and that keeping the original is the better choice. The percent on the row comes from the two measured byte counts through one function, whose formatting is shown here on invented numbers only: 2,480,000 to 1,730,000 bytes reads "30% smaller"; 96,000 to 101,000 reads "5.2% larger — keep your original"; an unchanged count reads "same size". The site publishes no blanket compression percentages anywhere, because no percentage is true for every image.

    What this tool cannot know

    The detector reads a signature, not the settings the input was encoded with; the planner receives qualities and byte counts, never pixels; the verifier checks a format and two dimensions, not a picture. So the tool cannot tell you whether a result looks acceptable, whether the input had already been compressed hard, or whether a quality is "enough" for a destination — it knows only that one attempt was under a target and another was over. It does not know the destination's rules either: the target field is yours to fill, and nothing on this page sets one for you. For the same reason this page states no expected savings and no recommended quality; the only numbers it shows are measured from your file, or computed from the engine at build time as every figure above was.

    Common misreadings

    • "This page only takes JPEGs." It takes every format the site decodes; a non-JPEG is converted to JPEG first, flattened if its container could carry transparency, and the row and the filename say so.
    • "Quality 75 keeps 75% of the file." It is MozJPEG's parameter, not a fraction of anything about your file, its size or its detail.
    • "Progressive is a different kind of file." It is a scan order inside the same decodable JPEG; the row records which order was written.
    • "The suffix means it was compressed." -compressed means the download would otherwise carry the original's exact name. A PNG converted here gets .jpg and no suffix — poster.jpg — and was converted, not compressed.
    • "Reached the target, so the image is fine." Reaching the target is a statement about bytes. Nothing here judges appearance.
    • "It came out larger, so the tool failed." It means this encoder at this setting could not beat what the input already was. The row says to keep the original; that is the correct result, not an error.
    • "Compressing it again will shrink it again." Each pass re-encodes already-encoded pixels; the measured bytes decide, and growth is reported as plainly as shrinkage.

    Cut pixels before bytes with the Image Resizer; this page never changes dimensions, and the output is verified to decode at exactly the input's. The Image Compressor is the canvas lane, with WebP output and a transparency-keeping route for PNG. JPG to AVIF writes a different format with the site's other self-hosted encoder, and its page says what that costs in encoding time and why it offers no target-size mode. The guide behind this page is Quality parameters and compress-to-target; How image formats are detected and The conversion pipeline cover the two things this page does before it encodes.

    Frequently asked questions

    How is this different from the Image Compressor on this site?

    The Image Compressor hands your pixels to your browser’s built-in canvas encoder and also handles WebP and PNG. This page is JPEG-only but encodes with MozJPEG, which does encoder-level work the canvas encoder does not — trellis quantization, optimized Huffman tables, progressive scans. The engine’s own claim is narrow: the same decodable JPEG with measurably fewer bytes at the same quality setting. Every result row shows the measured difference on your actual file; nothing is promised in advance.

    What is a progressive JPEG, and why is it on by default?

    A baseline JPEG is written in one top-to-bottom scan; a progressive JPEG is written in passes, so the whole image can appear early at low detail and sharpen as the rest loads. The lane record in the engine turns progressive order on by default and describes it as smaller files and progressive rendering; untick the box and MozJPEG writes baseline order instead. Both are standard, decodable JPEGs, and the row’s note records which one was written beside the quality used.

    How much smaller will my file get?

    It depends entirely on the photo and how it was encoded before, so this page refuses to promise a number. Instead it measures: each row shows your file’s real input bytes, output bytes, and the percent change computed from those two counts. If the result comes out larger — which the tool checks for on every file — the row says so and recommends keeping your original.

    What does the target file size mode do?

    It answers "the form says maximum 200 KB". The tool encodes at the top of the quality range, measures the real bytes, and binary-searches the quality setting — bracketing between the highest quality known to fit and the lowest known to overshoot — converging within eight encodes, every one measured. The row then states the target, the size reached, the MozJPEG quality that reached it, and how many encodes it took. If the target is unreachable even at minimum quality, it says so and offers the smallest file it could produce.

    Can I compress a PNG or HEIC file here?

    You can drop one, and it is converted to JPEG first — that is the only way a JPEG encoder can compress it, and the row discloses the conversion. Transparency is flattened onto the background colour you choose, because the capability table says JPEG cannot hold an alpha channel. To keep transparency, use a target that can: WebP via the Image Compressor or AVIF via PNG to AVIF, both of which the table marks as alpha-capable.

    Is my photo uploaded anywhere?

    No. The MozJPEG encoder is WebAssembly served from this site and running in your browser tab. The site’s security policy blocks requests to any other origin — verifiable in your browser’s network inspector.

    Why does the download have "-compressed" in its name?

    Because the output keeps your file’s base name and takes the canonical .jpg extension, and when that would reproduce the original name exactly, the -compressed suffix is inserted so the download cannot overwrite the file it came from. Computed by the same function the tool calls, on invented names: receipt.jpg becomes receipt-compressed.jpg, and Scan.JPG becomes Scan-compressed.jpg because the comparison ignores case, while portrait.jfif becomes portrait.jpg and poster.png becomes poster.jpg with no suffix, because the extension already changed.

    What happens to an animated GIF or WebP?

    The plan for WebP, GIF, AVIF sources carries a first-frame flag, because those containers can be animated and decoding keeps only the first frame. The detector checks before anything is decoded — image descriptors are counted for GIF, the VP8X animation flag and ANIM chunks are read for WebP, and the avis brand is read from an AVIF’s ftyp box — and the row says so up front when animation is found. The compressed output is a still JPEG of that first frame. Still files carry no such notice.

    Local processing: your photos are compressed and verified inside your browser and never transmitted. JPEG encoding by MozJPEG (this site includes the libjpeg-turbo software), self-hosted and built from pinned upstream source — see the methodology page for full attribution and how to verify the no-upload claim yourself.