A batch image converter that runs entirely in your browser. Your files are decoded, resized and re-encoded in the tab's own memory — nothing is uploaded, and there is no server to upload to.
Convert: HEIC · PNG · JPG · WebP · AVIF · GIF · BMP · ICO · SVG
Most online image converters ask you to upload your photos to a server you know nothing about. For a holiday photo that might be fine. For a passport scan, a medical form, or a screenshot of something confidential, it is not.
ImgType never sends your images anywhere. The site is a static bundle; once it has loaded, every byte of work happens locally. You can verify this yourself in about thirty seconds — see Verifying the claim.
- Batch conversion with per-image overrides for format, quality, rotation and flip
- Metadata inspector — see the GPS coordinates, camera serial number and timestamp buried in your photo before they're stripped
- Aim for a file size — say "under 200 KB" and it searches for the best quality that fits
- Presets for common jobs — web-optimised WebP, lossless PNG, compact JPG, 32×32 favicon
- Resize by percentage, longest-edge cap, or exact dimensions
- Before/after comparison with a draggable divider that also works by keyboard and on touch
- ZIP export of the whole queue
- Paste from clipboard with
Ctrl+V/⌘+V - HEIC/HEIF input decoded locally, so iPhone photos work without any conversion service
- Works offline — installable, and survives a reload with the network off
- A live network request counter in the footer, so the privacy claim is something you watch rather than something you're told
Not every format is created equal in a browser, and ImgType is explicit about the difference:
| Format | How it is written | Notes |
|---|---|---|
| PNG, JPG, WebP | The browser's built-in encoder via canvas.toBlob |
Universally available |
| AVIF | @jsquash/avif (libavif compiled to WebAssembly) |
No browser can encode AVIF via canvas; the codec downloads on first use |
| GIF | Hand-written encoder using omggif |
256 colours, single frame |
| BMP | Hand-written 24-bit encoder | Uncompressed, no transparency |
| ICO | Hand-written container wrapping a PNG | Keeps transparency; readable on Vista+ |
| SVG | The rendered raster embedded in an SVG document | Not a vector trace |
| HEIC | Not offered as output | Patent-encumbered; no browser can encode it. Input works fine |
Formats the browser cannot encode are disabled in the UI, and every download is named after the format that was actually written — never the one that was requested.
Add a photo straight from your phone and look for the Contains GPS location badge. ImgType parses the EXIF locally and shows you the coordinates, the camera and lens, the capture time, and the camera's serial number — the things that quietly travel with every photo you send anyone.
Converting redraws the image onto a fresh canvas, so all of it is discarded. That is currently unconditional: there is no option to preserve metadata, because re-injecting it into a canvas-encoded file is a separate job that hasn't been done yet.
Don't take the README's word for it. In rough order of how convincing they are:
1. The live request counter. The footer shows how many network requests have happened since the app loaded, read from the browser's own Resource Timing buffer rather than from ImgType reporting on itself. Convert as much as you like; it stays at zero.
2. Airplane Mode, including a reload. Disconnect your network, refresh the page, then convert an image. It still works — ImgType installs a service worker, so the whole app lives on your device. Something that needed a server could not do that.
3. Try to make it leak. Paste this into the DevTools console:
fetch('https://example.com', { method: 'POST', body: 'x' })The browser refuses it. A Content-Security-Policy restricts connect-src to this origin and
the two GitHub hosts used by the verification buttons. That is not a promise about what the code
chooses to do — it is the browser preventing it, and it binds every dependency too.
4. Verify the running code against the published build. The Source tab hashes every file
this page loaded with SHA-384 and compares it against a manifest CI publishes to a separate
integrity branch on GitHub. A mismatch means the host served something the build did not
produce. The manifest deliberately comes from a different origin than the site, because a host
serving tampered JavaScript would happily serve a tampered same-origin manifest too.
What that last check cannot do: prove the build itself is honest (read the source for that), or catch a browser extension rewriting the page after load.
- The two buttons on the Source tab, which send only a repository name
- A one-off codec download, from this same site, the first time you convert a HEIC or an AVIF
That's the complete list, and the counter shows you each one as it happens.
npm install
npm run dev # http://localhost:3000
npm run lint # tsc --noEmit
npm run build # production bundle in dist/
npm run preview # serve the built bundleBuilt with React 19, TypeScript, Vite and Tailwind CSS v4. Deployed to GitHub Pages by
.github/workflows/deploy.yml on every push to main.
The initial download is ~120 KB gzipped. The two WebAssembly codecs are much larger — libheif for HEIC input, libavif for AVIF output — so they are dynamically imported and left out of the install-time precache. You only download one if you actually use that format, after which the service worker keeps it for offline use.
npm run build also writes dist/integrity.json, the SHA-384 manifest that powers the
verification described above. CI publishes a copy to the integrity branch.
The deploy workflow needs contents: write in order to publish that manifest branch. If you fork
this, the integrity branch is created automatically on the first deploy.
Don't delete the
integritybranch. It is not a leftover feature branch — it holds the manifest the Verify button fetches, and it deliberately has unrelated history, one file, and a large "behind main" count that grows with every commit. Removing it silently disables verification until the next deploy recreates it. It lives on GitHub rather than indist/on purpose: a host that tampered with the JavaScript would serve a tampered same-origin manifest too, so checking the site against a file the site controls would prove nothing.
- Web Worker pool so large batches never block the UI
- A visual difference heatmap, so you can see where compression hurt rather than guessing at a quality number
- An option to preserve metadata rather than always stripping it
- Content-aware format suggestions ("this has transparency — JPG will give you a black background")
ImgType's first version was generated through Google AI Studio, and a significant part of what it told users about itself was untrue — including a "source verification" panel that displayed hardcoded hashes and a permanent green "In Sync (100% Match)" badge without ever hashing or comparing anything.
That is written up in docs/verification-and-honesty.md: what was fabricated, why that category of bug is worse than an ordinary one, and how to review for it. Everything it described is now either implemented for real and tested against its failure path, or removed.
MIT — see LICENSE. Do what you like with it; keep the copyright notice.