resopt
Find smaller app resources, review the real candidates, and apply only what you approve — with every change reversible.
resopt scans an Xcode, Swift package, Android or plain resource directory, measures what each image and animation could shrink to, and opens a local review page with previews, quality scores and one-click apply and restore.
Your files never leave your computer. Analysis never modifies your project.
What you can do
- Analyze a whole project. Asset catalogs, loose resources, Android
res/andassets/. Git ignore rules are respected by default, including nested rules, negations and force-tracked files. - See results while analysis runs. Completed files appear immediately, largest first. Stop at any time; finished work is cached, so the next run continues where you left off.
- Optimize PNG without changing a pixel. Decoded samples (including color under transparent pixels) and metadata chunks are verified before a candidate is offered.
- Compare JPEG, HEIC and WebP candidates at the encoder quality levels you choose (75, 85 and 95 by default), including same-format recompression of existing JPEG and WebP files. Lossless WebP candidates are verified sample-for-sample.
- Optimize SVGA animations losslessly. Embedded images are recompressed; every other byte of the animation and every pixel is verified unchanged.
- Judge quality with evidence. Side-by-side previews, a full-size comparison slider, SSIMULACRA2 perceptual scores, and RGB and Alpha error for every candidate.
- Decide on warnings yourself. Candidates below the perceptual-score or Alpha thresholds are kept, clearly marked, and excluded from recommended totals. You can accept one after reviewing it; your approval is recorded. Corrupt files, changed dimensions, stale files and protected resources can never be approved through.
- Apply safely, one file or many. Preview exactly which files change, confirm, and restore any time — even after restarting resopt. Batch apply takes an explicit policy, reports each file's outcome, can be stopped midway, and "Restore all" undoes everything. Files you edited after analysis are never overwritten.
- Keep references working. Asset-catalog
Contents.jsonentries and statically resolvable references in source, project and web files are migrated together with a format change, and restored together. - Find duplicate images. Identical files, the same picture saved at different sizes, and near-duplicates are grouped by comparing decoded pixels. Intended variants (
@2x/@3x, Android density folders) are not reported. - Inventory everything else. Audio, video, fonts, archives, SVG, PDF and data files are listed with an explicit "no optimizer" status. With
ffprobeinstalled, audio and video show codec, duration and bitrate. - Use it in scripts and CI. JSON output, a self-contained HTML report, and command-line batch apply/restore.
Install
Download a ready-to-run binary from GitHub Releases:
- macOS — Apple Silicon or Intel
- Linux — x86_64
- Windows — x86_64
Extract the archive and put resopt (or resopt.exe) on your PATH. Binaries need no Rust, Node.js, Bun, ffmpeg or Android SDK. Verify a download with the published SHA256SUMS.
With Rust installed:
The package is named resopt-cli; the command is resopt. Building from source needs Rust 1.89+ and a C compiler.
Start with your project
resopt opens your browser on a page served from 127.0.0.1 only. The printed URL contains a session key; the page and its data are not served without it. Results stream in as files finish. When analysis completes you can compare candidates, apply a change, restore it, or batch-apply under a policy you choose.
The terminal prints the report directory. It holds the report and the restore backups — keep it for as long as you may want to undo changes. To reopen it later (open the URL it prints):
Useful options
Quality values are encoder parameters, not savings percentages. --min-score is the lowest SSIMULACRA2 score a lossy candidate may have and still be recommended (100 is identical, 90+ is usually imperceptible).
Use --jobs to set parallel workers (default: CPU count, up to 8), --max-pixels for very large images, and --no-cache to skip the result cache. resopt cache prints the cache location; resopt cache --clear empties it.
Reports, scripts and CI
apply on a report never applies lossy candidates, format changes or warning candidates unless you pass --lossy, --cross-format or --accept-warning <kind>. Each file is applied as its own recoverable operation and gets its own outcome line; the command exits non-zero if any file failed.
The original PNG-only plan workflow is still available: resopt plan, then resopt apply <plan> and resopt restore <plan>.
Android projects
resopt treats Android resources as resources, not loose files:
- Files under
res/are identified by source set, resource type, qualifiers (density, RTL, locale, API level) and resource name. - PNG → WebP keeps the resource name, so
@drawable/nameandR.drawable.namekeep working and no source file is rewritten. A second file with the same name in the same directory blocks the change. The preview shows how many XML and code references use the name and flagsgetIdentifier()lookups. - WebP is gated by
minSdk, read from Gradle files and version catalogs: lossy WebP needs API 14, lossless or transparent WebP needs API 18. IfminSdkcannot be determined, WebP is not proposed until you pass--android-min-sdk. - Nine-patch (
.9.png) files stay PNG so AAPT can read their stretch and content markers; they still get pixel-identical PNG optimization. Launcher icons (mipmap-*) andres/rawfiles also keep their format. The reason is shown wherever a change is blocked. - JPEG and HEIC replacements are never proposed for Android resources.
- Files under
assets/are opened by path, so a format change migrates path references like a loose file — one file at a time, never in a batch, because asset paths are often built at runtime. - With the Android SDK build-tools installed, every
res/candidate is compiled with AAPT2 before it is applied, and the preview shows the measured compiled size. This matters: AAPT2 re-compresses PNG files during the build, so source savings on PNG do not translate one-to-one into the APK.
To measure what actually changed in a build, compare two packages:
Platform support
| Capability | macOS | Linux / Windows |
|---|---|---|
| Project inventory, Git ignore rules, duplicate detection | Yes | Yes |
| PNG lossless optimization | Yes | Yes |
| SVGA lossless optimization | Yes | Yes |
WebP candidates (--webp), lossy and lossless |
Yes | Yes¹ |
| JPEG and HEIC candidates; decoding JPEG/HEIC/GIF/TIFF inputs | Yes (Apple ImageIO) | No |
| Local review page, apply, batch, restore | Yes | Yes |
AAPT2 validation (optional Android SDK), ffprobe media details (optional) |
Yes | Yes |
¹ Without ImageIO, WebP conversion accepts PNG and WebP inputs that carry no embedded color profile or EXIF orientation; other inputs are reported as unsupported rather than converted incorrectly.
resopt doctor shows what is available on your machine and how to install optional tools. resopt never installs anything itself.
A browser-only edition (static site, WebAssembly) optimizes individual PNG files entirely inside the browser. It has no server component and nothing is uploaded. Whole-project scanning, other formats and applying changes need the resopt command.
Important limits
- Savings are source-file bytes. They are not IPA/APK size or store download size: Xcode compiles asset catalogs and AAPT2 re-compresses PNGs. Use
package-diffon real builds to measure shipped size. - The inventory lists files on disk. It does not know which files a particular build target, flavor or variant includes.
- A perceptual score helps you prioritize; it does not replace looking at the image, especially for UI art with fine edges.
- App icons, sliced (resizable) catalog images and animated images are inspected but never converted. Animated images are never flattened.
- WebP is not offered for asset-catalog renditions.
- Reference migration covers statically resolvable references. Names built at runtime, third-party decoders and references outside the scanned directory need your review; ambiguous references block the change instead of guessing.
- SVGA 1.x (zip) files, and SVGA files containing audio or unknown fields, are reported as unsupported rather than rewritten.
- SVG, PDF, audio, video, fonts and archives are inventoried but not optimized. No lossy audio/video transcoding is performed.
- HEIC candidates cannot be displayed by most browsers; the comparison uses a PNG preview and links the file so you can open it in Preview or Safari.
Run resopt --help or resopt <command> --help for every option.
Changelog · Development guide · Architecture · Validation evidence · Performance · MIT license