🧬 IME

IME (Image Metadata Editor) is a fast, lightweight, and portable CLI written in Rust for reading, writing, and wiping metadata in .png, .jpg, and .webp files, with JSON input and output. It includes a built-in interactive terminal UI (TUI) for browsing and editing metadata visually.

Zero loss in quality: ime edits only metadata and never touches your pixels.

Installation

Pre-built binaries

Download a pre-built executable for your operating system from the Releases page.

Currently supported platforms:

  • Linux (x86_64, ARM64, and ARMv7)
  • macOS (Intel and Apple Silicon)
  • Windows (x86_64)

Using Cargo

If you work in the Rust ecosystem, you can install the latest version directly from crates.io using cargo.

cargo install ime

Using JavaScript package managers

If you work in the JavaScript ecosystem, you are probably familiar with package managers such as NPM, PNPM, Bun, and others. You can use them to install @avaray/ime globally or run it without installing it. The installed executable is always called ime.

# Install globally using NPM
npm install -g @avaray/ime

# Run instantly without installation using Bun
bunx @avaray/ime photo.jpg

Usage

ime <file> [OPTIONS]         Read or modify a single file (use - for stdin)
ime <dir> [--recursive]      Process every supported image in a directory
ime [--tui] [<path>]         Open the interactive TUI (defaults to current directory)
FlagShortDescription
--set <JSON>-sMerge a JSON object into the metadata; repeatable
--wipe-wRemove all metadata from the file
--dry-runPrint the would-be result without writing anything
--output <path>-oWrite the result to <path> instead of in-place (- for stdout)
--recursive-rRecursively process subdirectories when a directory is provided
--tui-tOpen the interactive terminal UI for the given path
--power-pTUI power mode: skip confirmations and auto-save on quit
--watchTUI-only: live-refresh the file list as files change
--expand-xTUI-only: show inline branch previews in the metadata panel
--version-vPrint the semver version number and exit
--help-hPrint help and exit

Reading metadata

Prints all extracted metadata as pretty-printed JSON with two sections: exif for standard EXIF tags and custom for user-defined keys.

ime photo.jpg
{
  "custom": {
    "UserComment": {
      "project": "stage1",
      "rating": 5
    }
  },
  "exif": {
    "Make": "TestMake",
    "Model": "TestModel X1",
    "ExposureTime": "1/200",
    "GPSLatitude": ["52/1", "13/1", "0/1"]
  }
}

The custom shape is format-dependent: JPEG/WebP expose the single UserComment slot, while PNG exposes every native text chunk under PngText.<keyword> (e.g. custom.PngText.workflow for a ComfyUI file).

Values keep their native EXIF shape: rationals stay as "numerator/denominator" strings, dates stay as "YYYY:MM:DD HH:MM:SS", and GPS coordinates stay as degree/minute/second triplets. A string value that itself holds a JSON object or array (such as an embedded ComfyUI workflow document) is parsed and shown as nested JSON.

Setting metadata

--set deep-merges a JSON object into the file’s metadata: keys present in the input overwrite the corresponding value, and everything else is left untouched. Repeat the flag to apply several payloads in order.

# Tag a photo (JSON5 syntax: unquoted keys, single quotes, trailing commas)
ime photo.jpg --set "{exif: {Artist: 'Jan Kowalski'}, custom: {UserComment: {rating: 5}}}"

# Delete keys by setting them to null (or "", {}, [])
ime photo.jpg --set '{"exif": {"GPSLatitude": null, "GPSLongitude": null}}'

# On PNG, custom keys live under PngText (one native text chunk per keyword)
ime image.png --set '{"custom": {"PngText": {"comment": "vacation photo"}}}'

# Read the payload from stdin or from a file instead of inline
echo '{"custom": {"UserComment": {"batch": 7}}}' | ime photo.jpg --set -
ime photo.jpg --set @tags.json

Wiping metadata

Removes every standard EXIF tag, every custom key, the ICC color profile, and any other removable metadata, in-place by default.

# Strip everything, keeping only the pixels
ime photo.jpg --wipe

# Write the cleaned copy elsewhere, leaving the original untouched
ime photo.jpg --wipe -o clean.jpg

Previewing changes

--dry-run runs the full --set/--wipe logic in memory and prints the resulting metadata, without writing anything to disk.

ime photo.jpg --set '{"exif": {"Make": "Ghost"}}' --dry-run

Piping

<file> may be - to read the image from stdin, and --output - writes the resulting image to stdout. With stdin input and no --output, the result goes to stdout by default.

curl -s https://example.com/photo.jpg | ime - --set @tags.json > tagged.jpg

Batch processing

Pass a directory to process every supported image it contains. Reads print an aggregated { "<path>": <metadata> } object; writes happen in-place with per-file progress on stderr. Use --recursive to descend into subdirectories (symbolic links are followed).

ime ./photos --set '{"exif": {"Copyright": "2026 Jane Doe"}}' --recursive

A single bad file never stops the batch: it is reported on stderr, the rest continue, and the exit code is 1 if anything failed.

Interactive TUI

ime includes a built-in terminal UI for browsing directories and editing metadata visually.

ime                  # browse the current directory
ime ./photos --tui   # browse a directory
ime photo.jpg -t     # open with a file pre-selected
KeysAction
Up/Down, Left/RightNavigate the file tree / drill through metadata
Ctrl+Left/Ctrl+RightJump to root / Jump back to deep path
TabSwitch between the file and metadata panels
Enter, eEdit a value / edit a subtree as JSON
nNew entry in the current branch
dDelete entry (with confirmation)
wWipe the selected file (with confirmation)
/Search the focused panel
c, Ctrl+CCopy a value / copy a path
rRe-scan the current directory
Ctrl+SCommit in-memory edits to disk
F1Help overlay (keybindings & project info)
qQuit

Supported formats

Only PNG, JPEG, and WebP are supported. Formats are detected by magic bytes, never by file extension — a PNG renamed to .dat still works, while any other file kind (including HEIC, AVIF, TIFF, RAW, and all video formats) is rejected and never written.

Exit codes

CodeMeaning
0Success
1Runtime error (I/O error, unsupported format, corrupt file, invalid --set JSON, unknown EXIF tag)
2Usage error (bad flags, missing file argument, incompatible flag combination)

Notes

  • ime was originally created as a dependency for my custom ComfyUI node collection, which is not publicly available yet.
  • I do not plan to expand metadata-writing support to additional file formats, especially video formats.

Changelog

All notable changes to this project will be documented in the CHANGELOG.md file.

License

This project is licensed under the CC-BY-NC-4.0 License.