Turn an image into a data URL whose payload is decoded again and compared byte for byte with the file, with the media type taken from the file's own signature rather than its extension and the size cost of embedding stated in full.
Initializing in your browser…
Convert between the formats a browser can really write, then open the output and name the format its first bytes say it is. AVIF, BMP and GIF are disabled because a canvas returns a PNG for all three.
Resize with the filter named (Lanczos, Mitchell, Catmull-Rom, bilinear, box or nearest) and the option to average LIGHT rather than stored bytes, which is worth 1.24 stops on a detailed picture.
Whole-pixel crops with the rectangle typed or dragged, checked by decoding the output and comparing it with the pixels that went in. Quarter turns and mirrors are exact permutations, so nothing is clipped.
A 16 by 16 PNG of 138 bytes, saved under the name photo.jpg.
Input
photo.jpg · 138 bytes on disk
What the page reports
First bytes 89 50 4E 47 0D 0A 1A 0A
Signature says PNG
Browser said image/jpeg (from the NAME)
Data URL uses image/png (from the BYTES)
Round trip 184 base64 chars decoded again:
all 138 bytes identicalA browser fills a file’s type in from its extension, so a renamed PNG is handed over as image/jpeg and the data URL says so. The type here comes from the first bytes instead. The round trip is the other half: the payload is decoded again and compared with the file, so "this string is your image" is a check rather than a claim.
Turn an image into a data URL that decodes back to the exact bytes of your file, with that check performed and reported rather than assumed. The media type comes from the file's own signature rather than from its extension, and the size cost of embedding is stated in full.
One fewer request for a sprite or a small background, with the size cost of doing it shown before you commit.
A report or an email template that has to travel as one file, with the payload verified before it goes in.
The first bytes and the format they name, beside the type the browser guessed from the extension.
The percent-encoded form, which is shorter than base64 and still readable in the stylesheet.
Upload a small PNG, pick the CSS form, and paste the declaration. The page has already decoded the payload again and confirmed it is byte for byte the file you gave it.
A PNG saved as photo.jpg: the browser calls it image/jpeg, the first bytes say 89 50 4E 47, and the data URL is written as image/png with the disagreement named on the page.
A base64 tool makes one claim: that the string decodes back to the file. That is decidable, and the way to settle it is to decode the string again and compare byte for byte, which this page does and reports. On every fixture in the test suite, from a 138 byte PNG to a 9000 by 6000 JPEG, the payload decodes to a file identical to the one on disk, confirmed both here and independently by Python's own `base64` module with validation on. Reporting that the encoding is CORRECT is worth as much as reporting a fault would have been, because it is the thing nobody checks.
What was wrong sat around the encoding. The media type in a data URL used to come from `File.type`, and a browser fills that in from the file EXTENSION rather than from the contents. A PNG renamed to `.jpg` was therefore handed out as `data:image/jpeg;base64,iVBORw0KGgo...`, a JPEG declaration in front of PNG bytes. The type now comes from the file's own first bytes through a signature sniffer that knows seven formats, the first bytes are printed on the page, and when the name and the signature disagree the page says so with both values. Browsers sniff and cope; a strict consumer, a server that trusts the declared type, or a content policy that filters on it does not.
The cost of embedding is stated rather than left to be discovered. Base64 is exactly four characters for every three bytes, so a data URL is about a third larger than the file it carries, and the page shows the file size, the payload length, the whole URL length and the overhead as a percentage. It also says the part that is not arithmetic: an embedded image cannot be cached separately from the document it sits in, which is the trade being made for one fewer request.
Resizing before encoding is now honest about what it is. Any resize decodes the picture, scales it and encodes a NEW one, so the string no longer represents your file, and the page says exactly that with the new dimensions and the format. The scaling goes through this project's resampler with the filter named and cited rather than through a bare `drawImage`, whether the quality setting does anything is MEASURED by encoding at both ends of the range (it does nothing at all for PNG, and the page says so rather than leaving a useful-looking control that is inert), and the cost is reported as SSIM, PSNR, the worst channel difference and the share of pixels that moved.
Five output shapes are offered, all carrying the same payload: the whole data URL, the raw RFC 4648 payload, a CSS `background-image` declaration, an `<img>` tag and Markdown. For an SVG a sixth appears, because RFC 2397 allows the data to be percent-encoded instead of base64, and for XML that is both smaller (base64 costs a flat third; percent-encoding costs only the characters that need escaping) and readable in the stylesheet. The preview is drawn from the data URL itself rather than from the file, so what you see is what the string produces. Nothing is uploaded.
Yes, and the page checks rather than claiming. After encoding, the payload is decoded again and compared byte for byte with what went in, and the result is reported either way. In the test suite the same check is done independently by Python, on files from 138 bytes to 1.5 MB, and every one matched.
Because the extension can be wrong. A browser fills in a file's type from its name, so a PNG renamed to .jpg is handed over as image/jpeg. This page reads the first bytes instead and writes what the file actually is, printing both and naming the disagreement when there is one.
Base64 is four characters for every three bytes, so about 33 percent, plus the header. A 613 KB JPEG becomes 837,084 characters. The page shows all four numbers. The second cost is not size: an embedded image cannot be cached separately from the page it sits in.
Only if you want a different picture. A resize decodes, scales and encodes again, so the string is no longer your file, and the page says so with the new size and format and reports what the re-encode cost in SSIM and PSNR. With the width at zero nothing is touched.
Because nothing is being encoded again. It becomes active when a resize is set, and even then the page measures whether it does anything: PNG ignores it entirely, giving the identical byte count at 0.1 and at 1.0, and the page says that instead of leaving a control that looks useful and is not.
Practically, your browser's memory. A 9000 by 6000 photograph encodes here without trouble. The thing that bites first is usually the page you paste it into: a two megabyte string in a stylesheet is a two megabyte stylesheet.
Images are decoded, edited, and exported entirely inside this browser tab. No originals, exports, or metadata are uploaded.