Skip to content

Repository files navigation

ImgType

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


Why client-side

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.

Features

  • 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

How each format is produced

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.

Metadata

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.

Verifying the claim

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.

Everything that can touch the network

  • 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.

Development

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 bundle

Built 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.

Deployment note

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 integrity branch. 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 in dist/ 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.

Roadmap

  • 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")

Project history

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.

License

MIT — see LICENSE. Do what you like with it; keep the copyright notice.

About

Convert Images Directly in Local Memory No server uploads. Zero network overhead. Media is processed instantly in browser RAM using HTML5 Canvas & WebAssembly encoding.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages