A fully client-side, privacy-first web app to inspect, edit, remove and export metadata from virtually any file type — images, video, audio, documents, archives and more.
Everything happens entirely inside your browser. No files are uploaded anywhere. No analytics. No telemetry. No tracking. Works offline as a PWA.
Built with Vue 3 + Vite + TypeScript + Pinia + TailwindCSS. The metadata
engine is the real ExifTool 13.x compiled to WebAssembly
(@uswriting/exiftool), run inside a Web
Worker so the UI never freezes — giving you exactly what the exiftool command
line shows, in the browser.
| Area | What you get |
|---|---|
| Inspect | Everything ExifTool reads — EXIF · IPTC · XMP · ICC · GPS · PNG chunks · JPEG segments · ID3/Vorbis/FLAC · MP4/QuickTime atoms · Matroska tags · PDF & Office properties · C2PA / JUMBF provenance · maker notes · hundreds more · plus SHA-256 & magic bytes |
| Views | Flat (verbatim exiftool Tag : Value) · Grouped · JSON · Hex — with search, filtering, collapse/expand, copy value / copy all / copy JSON |
| Edit | Writable fields across many formats (images, PDF, audio/video) via ExifTool writeMetadata, with validation |
| Remove | EXIF · GPS · XMP · IPTC · thumbnails · comments · maker notes · all · deep clean |
| Batch | Drag whole folders, multi-select, bulk deep clean → ZIP download |
| Compare | Original vs. cleaned/edited metadata diff (added / removed / modified) |
| Preview | Inline image / video / audio player and PDF viewer |
| Export | Metadata as JSON · TXT · CSV; download cleaned/edited files |
| UX | Dark/light mode · mobile responsive · keyboard shortcuts · drag-and-drop · accessible (focus rings, ARIA, skip link, high-contrast aware) |
/ focus search · 1–4 Flat / Grouped / JSON / Hex · j/k next/previous file ·
d toggle theme · esc clear search
src/
├─ components/ Reusable Vue components (viewer, panels, dropzone, …)
│ └─ viewer/ Flat / Grouped / JSON / Hex views + viewer toolbar
├─ composables/ useDropzone, useClipboard, useKeyboard
├─ parsers/ capabilities (writable formats) + exiftoolJson (normalise)
├─ services/ exiftool.ts (engine wrapper) + workerClient (promise API)
├─ stores/ Pinia stores (files, ui)
├─ types/ Shared TypeScript types
├─ utils/ format, hex, hash, export, files helpers
├─ views/ Router views (Home, About)
└─ workers/ metadata.worker.ts — runs ExifTool off the main thread
Flow: a dropped file → stores/files reads its bytes (computing SHA-256 /
magic bytes / hex preview) → services/workerClient transfers the ArrayBuffer
to workers/metadata.worker → the worker runs ExifTool (services/exiftool.ts)
and parsers/exiftoolJson.ts normalises the output into grouped + flat + JSON
form. Edits/cleans call ExifTool writeMetadata and the result is re-parsed for
Compare mode. The ~25 MB wasm engine is lazy-loaded on first use (with a
progress bar) and runtime-cached for offline use.
@uswriting/exiftool — ExifTool 13.x via
WebAssembly (@6over3/zeroperl-ts) · JSZip for
batch ZIP export. Both run fully in the browser.
npm install
npm run dev # http://localhost:5173Build & preview the production bundle:
npm run build # type-checks then builds to dist/
npm run previewThe output is a static site in dist/. It uses hash-based routing, so it
works on any static host with no server rewrite rules.
A workflow is included at .github/workflows/deploy.yml. Push to main and enable
Pages → "GitHub Actions". For a project page (user.github.io/repo/) the base
path is set automatically from the repo name via VITE_BASE_PATH.
# Manual build for a project sub-path:
VITE_BASE_PATH=/your-repo/ npm run build- Build command:
npm run build - Build output directory:
dist - (Custom domain / root deploy: leave
VITE_BASE_PATHunset.)
vercel.json is included. Framework preset: Vite. Output dir: dist.
netlify.toml is included. Build command npm run build, publish dist.
There is no backend. Files are read with the File/Blob APIs, processed in a
Web Worker, and never leave the device. The only network requests are for the
static app assets themselves (and none at all once the PWA is cached). The app
ships with no analytics, trackers or third-party beacons.
- First load: the ExifTool engine is a one-time ~25 MB (≈7.7 MB gzipped) WebAssembly download, fetched lazily on your first file with a progress bar and then cached (it is deliberately not in the service-worker precache, so the app shell stays tiny). After that it runs fully offline.
- Reading covers everything ExifTool supports.
- Editing / removal is enabled for ExifTool's writable formats (JPEG, TIFF, PNG, WebP, PDF, many RAW formats, MP4/MOV, MP3/FLAC…). Other containers are read-only in the browser — you can still inspect and export their metadata.
MIT