Local image conversion.

Verified October 2026

What this guide covers

The image converter is a pipeline of six stages: identify the file from its bytes, decode it to pixels, fit those pixels under a maximum width, encode them as one of four formats, check the encoded file against what was drawn, and name it so that it cannot overwrite the source. The tool page summarises what each stage does; this guide explains why each is built the way it is, where each one can stop, and how to read what the result line hands back. Every stage runs inside the browser tab on your own device. Every number here is imported from the engine, computed at build time from a fixed and labelled fictional input, or read from the specs registry with its date; the methodology documents the same rules beside those of every other tool.

Why the first bytes decide, not the extension

A file’s extension was chosen by whoever saved or renamed it, and the decoder that opens the file never reads it. What the decoder does read is the opening bytes, and each of the seven accepted formats begins with a pattern fixed by its own specification. The engine therefore takes a prefix of at most a fixed number of opening bytes from the file — a file shorter than that yields a shorter prefix — and compares it with those patterns in a fixed order. The first pattern that fits names the format; when none fits, detectImageFormat returns null and the file is refused however it is named.

The detector runs three times in all: twice on the source and once on the output. When files are chosen, the tool page runs it on every file and, at the first null, shows its own notice naming that file and the seven accepted formats, and takes none of the selection. When a conversion starts, the engine runs it again on the same prefix — it does not trust the page to have done so — and refuses with The file is not a supported image format. The third run, on the encoded output, is described under verification below. The tool page shows the plain cases; the rows here are the edge cases, built from constructed bytes and run through the engine at build time:

Constructed inputdetectImageFormat returns
A GIF87a header, the older GIF signaturegif (GIF)
A RIFF container whose form type is WAVE, not WEBPnull — refused as unsupported
The letters BM followed by only four more bytesnull — refused as unsupported
An ftyp box with the brands heix, mif1heic (HEIC)
An ftyp box with the brands avis, mif1avif (AVIF)
An ftyp box carrying both avif and heicheic (HEIC)
A PDF header (%PDF-1.7) saved with a .jpg extensionnull — refused as unsupported
An empty filenull — refused as unsupported

GIF is matched on either of its two headers, the older one shown above and the newer. A RIFF container is WebP only when its form type says so, which is why a WAVE file with an image extension is caught. The BM check requires a minimum length, so a header cut short is refused rather than guessed at. The ISO base-media rules run in a fixed order — any HEVC brand names HEIC, then an AVIF brand names AVIF, then a bare mif1 or msf1 falls back to HEIC — which is why a box carrying both families reads as HEIC. The last two rows show a non-image caught before any decoder opens it; the empty file is the degenerate case, refused because there is nothing to match.

What goes in (seven formats) and what comes out (four)

The engine labels its inputs PNG, JPEG, WebP, GIF, BMP, HEIC and AVIF. Each output has a MIME type the file is written with, an extension the name is given, and an encoder route — the browser’s own, or one this site serves — and the first two are read from the engine’s own maps:

OutputMIME typeExtensionEncoded by
PNGimage/png.pngthe browser's canvas encoder
JPEGimage/jpeg.jpgMozJPEG, served from /vendor/mozjpeg/
WebPimage/webp.webpthe browser's canvas encoder
AVIFimage/avif.avifthe AVIF encoder, served from /vendor/avif/

Three of the inputs — GIF, BMP and HEIC — have no encoder route in the engine, so a file in one of them can be read and must leave as something else. The reverse case, a target that matches the source such as PNG to PNG, is not a shortcut: the file is decoded, fitted to the width, re-encoded and verified like any other, and the naming rule below guarantees the result does not replace the original.

The converter itself holds no figure about any platform. Two entries in the specs registry bear on how it is used, and the width examples below take their maximum-width setting from the first of them, so a change to that entry changes the examples:

FigureAs the official page states itVerified
1080 pixels is the maximum width Instagram keeps for feed photos“we make sure to upload it at the best quality resolution possible (up to a width of 1080 pixels).”2026-10-02
Image formats such as JPG or PNG“Be uploaded in image formats such as JPG or PNG.”2026-10-02

Verify against the platform’s own documentation before a critical print run or launch.

Two decoders: the browser's, and one the site brings along

Six of the seven inputs are decoded by the browser itself. The engine passes the file to createImageBitmap and asks for the orientation recorded in the file to be applied before the pixels come back, so the canvas receives the picture the way it was meant to be seen and the output needs no orientation flag of its own. A failure here is reported in one of two sentences. For AVIF it reads This browser cannot decode this AVIF image. Use a current Chrome, Edge, Firefox, or Safari release. — AVIF decoding is left to the browser and the converter bundles no substitute, so a browser without an AVIF decoder cannot take AVIF in. For any other browser-decoded format it reads The browser could not decode this image.

HEIC is handled differently because the engine never asks the browser to decode it. The whole file — not a prefix — is posted to a web worker, which fetches a HEIF decoder from /vendor/libheif/ the first time it is needed: 1.6 MB, measured from the actual files at build time, kept for as long as the worker lives. A HEIC container may hold several images. The worker asks the decoder which is marked primary, falls back to the first if none is, and checks that image’s width, height and pixel count against its per-side and megapixel ceilings before requesting any pixels; the remaining images are counted for the result’s note and otherwise left alone. A container holding no image at all fails with The HEIC decoder found no image. The decoded pixels return to the page, become a bitmap there, and from this point a HEIC source follows exactly the same path as every other.

The width rule, worked on fixed inputs

The converter has one size control, the maximum width, and the arithmetic behind it is three lines long. First a scale factor: the maximum width divided by the source width, capped at one. Then each dimension is multiplied by that factor and rounded to the nearest whole pixel, with a floor of one pixel so that nothing collapses to zero. That is the whole rule; there is no separate height limit and no fit-to-box.

On fixed fictional inputs with the maximum width set to 1080, the registry’s figure: a 3000×2000 photo becomes 1080×720, from 6,000,000 pixels to 777,600; a 1920×1081 frame becomes 1080×608, the height rounded to a whole pixel; an 800×1200 portrait stays 800×1200, already narrower than the setting; and a 1080×1080 square stays 1080×1080, because a factor of exactly one changes nothing.

Three things follow from the cap. The factor cannot exceed one, so no source is ever made larger — a maximum width set above the source width leaves the image as it is. Both dimensions take the same factor, so the aspect ratio cannot drift by more than the rounding of a single pixel. And height is never a trigger: a tall, narrow image passes untouched however tall it is, because only the width is compared with the setting.

One check sits between the arithmetic and the drawing. The engine multiplies the output width by the output height and compares the product with a fixed megapixel ceiling that it names in its own error message; above the ceiling the conversion stops before any canvas is allocated. For browser-decoded sources the ceiling is applied to the output, so a large source is brought under it by lowering the maximum width. A HEIC source is the exception: the decoder worker measures the primary image at its own size, against its per-side and megapixel ceilings, before decoding a pixel, and refuses it whatever the maximum-width setting — the message names the HEIC image itself, not the output. For JPEG and AVIF targets the encoder worker repeats the output check and adds a per-side ceiling, refusing with The encoder rejected invalid or oversized pixel data.if either is crossed; PNG and WebP outputs go to the browser’s encoder and meet only the megapixel check.

Encoding: four outputs, two routes out of the canvas

Once the picture is on a canvas there are two ways out. PNG and WebPleave through the canvas itself: the engine asks the browser to encode the canvas as the target’s MIME type, passing the quality setting as a fraction for WebP and no quality value at all for PNG — which is why the quality slider is disabled when PNG is the target. If the browser hands back no file the engine reports The browser encoder failed.

JPEG and AVIF leave through encoders this site serves itself. MozJPEG lives under /vendor/mozjpeg/ and weighs 0.4 MB; the AVIF encoder lives under /vendor/avif/ and weighs 3.5 MB; both sizes are taken from the vendored files with fs.statwhen the site is built, so they cannot go stale without the build noticing. Each encoder runs in a web worker of its own, fetched the first time its target is chosen and reused for the rest of the batch. The engine reads the pixel bytes back from the canvas and the worker passes the encoder four things and nothing more: the width, the height, those bytes, and an option object the engine fixes per codec. The slider value, once clamped to the engine’s own range, fills that object’s quality field — and, for JPEG, its chroma-quality field as well — and nothing else in it changes from one job to the next. Before calling the encoder the worker confirms that the byte count equals width times height times four, so a mismatched buffer is refused rather than read off the end.

Two further rules apply on either route. For a JPEG target the engine fills the canvas with your chosen background colour before drawing the picture over it, so whatever was transparent in the source takes that colour — this is the flattening the result line later reports. And every worker job runs under a time limit the engine sets per codec; a job that exceeds it is stopped and the error names the codec. Cancelling a batch stops the worker through the same mechanism; the reason the engine records for that stop reads The conversion was cancelled.

Verification: two checks before a file is offered

The engine does not assume an encoder did what it was asked. Two independent checks run on the encoded bytes, and each catches a different kind of failure. The first is the detector’s third run: the output’s own prefix goes through detectImageFormat, and the answer must equal the target, or the conversion fails with Output verification failed. This catches a file of the wrong type, whatever produced it. The second decodes the new file and compares its width and height with the values that were drawn; a mismatch fails with Output dimensions failed verification. This catches a file of the right type whose header or pixel grid does not agree with the canvas. A result is produced only when both agree, and the size the result line reports is the size of that verified file.

Naming comes last and is deliberately dull. Anything up to the final path separator is dropped, so only the file’s own name survives; whatever follows the last dot is treated as the extension, provided that dot is not the very first character; the target’s extension replaces it. One guard then runs: if the new name equals the source’s own name once case is ignored, -converted is inserted before the extension. From fixed names: Flyer.PNG to PNG becomes Flyer-converted.png, because the comparison ignores case; hero.jpeg to JPEG becomes hero.jpg, no suffix because the extension itself changes; C:\Users\promo\banner.heic to WebP becomes banner.webp, the folder part dropped; .hidden to WebP becomes .hidden.webp, because a leading dot is not an extension; and launch.v2.final.jpg to AVIF becomes launch.v2.final.avif, because only the last dot counts.

Reading the result line

A finished conversion carries the encoded file, its name, the output width and height, the label of the detected source format and a note. Two of those are easy to misread. The source format is what the detector found, not what the extension said, so a renamed file shows its real format here. And the note is assembled from up to three sentences in a fixed order — animation first, then the HEIC count, then flattening — joined with a space, or left empty when none applies; a WebP converted to JPEG, for instance, reads Animated sources export their first frame. Transparency was flattened onto #f4efe6.with the fictional background used in this guide. All but the HEIC count are attached by format, not by what the picture contains, so a note does not mean the picture lost anything. The table below is computed from the engine’s three conditions for every source and target:

Sourceto PNGto JPEGto WebPto AVIF
PNG—flattened——
JPEG————
WebPfirst framefirst frame + flattenedfirst framefirst frame
GIFfirst framefirst frame + flattenedfirst framefirst frame
BMP————
HEIC—flattened——
AVIFfirst framefirst frame + flattenedfirst framefirst frame
  • first frame is Animated sources export their first frame. It attaches to a WebP, GIF and AVIF source whatever the target, because those containers are able to hold animation; the engine never looks inside to see whether this one did. A still image in one of those formats carries the note and loses nothing.
  • flattened is Transparency was flattened onto followed by your background colour. It attaches to a JPEG target from any source that is able to carry transparency — which excludes JPEG and BMP — because the fill happened whether or not any pixel was transparent. An opaque PNG carries the note and the fill is entirely covered.
  • The HEIC count — the one note the format alone cannot predict — reads as the engine writes it, naming how many additional HEIC images the decoder found beyond the primary one. It appears only when that number is above zero.

Choosing a target: what the tool page says, and no more

The tool page offers three rules of thumb for picking an output, repeated here as written and not extended:

  • Use JPG for photographs. JPG keeps detailed photographic files small when transparency is not needed.
  • Use PNG for crisp graphics. PNG preserves hard edges, text, and transparency at a larger file size.
  • Use WebP for the web. WebP often gives websites a strong balance of visual quality and file size.

Where it stops

  • The scale factor is capped at one and applies to both dimensions, so the engine has no path by which a picture grows, is trimmed or is turned; the orientation it applies is the one recorded in a browser-decoded file, never one you ask for.
  • A file can leave only as PNG, JPEG, WebP and AVIF; GIF, BMP and HEIC are decoded, never encoded. Of an animated source, the first frame is what reaches the output.
  • Three refusals are about size, not content: an output whose pixel count exceeds the megapixel ceiling; for JPEG and AVIF targets, an output whose side exceeds the worker’s per-side ceiling; and a HEIC source whose primary image exceeds either ceiling at its own size, which no maximum-width setting can bring under. Each stops with a message; the engine never picks a smaller width on its own.
  • An encoder receives pixels and nothing else, so nothing a source file carried beyond its picture is in the output. The only network traffic a conversion causes is this site serving its own codec files to the worker; during a conversion the images themselves stay in the tab.

For sizing a converted image to a placement, see social image sizes and YouTube thumbnail specs; for the wider kit, promo design basics. Every rule above is stated from the engine in the methodology.

Questions

If I rename a .heic file to .jpg, what does the converter do with it?
It ignores the name, reads the ISO base-media brands from the first bytes, detects HEIC and decodes it with the bundled HEIF decoder. The result line shows HEIC as the source.
Why was my file refused when it opens fine elsewhere?
Because its first bytes matched none of the signatures the engine checks, so detectImageFormat returned null and the converter stopped with "The file is not a supported image format." Typical causes are a PDF or SVG given an image extension, or a RIFF file whose form type is not WEBP.
Why does a still WebP or AVIF say "Animated sources export their first frame."?
The engine decides that note from the source format alone and never opens the file to look for frames: a WebP, GIF and AVIF source always carries it, because those containers are able to hold animation. When the picture was still there was only one frame to keep, so the note describes nothing lost.
Can I use the converter to make a small image bigger?
No. The scale factor is the smaller of one and the maximum width divided by the source width, so it never exceeds one. In the fixed example above, an 800×1200 portrait with the maximum width set to 1080 comes back 800×1200.
What does the -converted suffix on an output name mean?
The suffix is the engine's guard against a result that would share its source's name once case is ignored: Flyer.PNG to PNG would otherwise be Flyer.png, so it becomes Flyer-converted.png. When the extension itself changes there is nothing to guard against, and hero.jpeg to JPEG is simply hero.jpg.
Is anything other than the pixels carried into the output?
No. For browser-decoded sources the bitmap is produced with the recorded orientation applied; a HEIC source arrives as the decoder's pixel grid. Either way the bitmap is drawn to a canvas. For PNG and WebP the browser's encoder receives that canvas with the target's MIME type and, for WebP, a quality fraction; for JPEG and AVIF the vendored encoder receives the width, the height, the pixel bytes read back from the canvas and the engine's per-codec option object. Nothing else from the source file reaches either.

Everything here runs in your browser: the tools compute on your device and the Studio’s package is assembled on the device. The methodology documents every rule the tools apply.