Batch image compression with a local web UI. Uses pngquant (lossy PNG quantization) and oxipng (lossless strip/optimization) behind a clean interface, plus WebP via Pillow, AVIF via avifenc, and — with mozjpeg's cjpeg — true lossy JPEG re-compression that keeps the JPEG format. Includes a dedicated Screenshot mode for near-lossless compression of UI/software screenshots.
- Local folder scanning — select a directory, scan recursively (optional), and pick which files to optimize
- File upload — drag or browse to upload individual images (PNG, JPG, BMP, TIFF, WebP)
- File sorting — sort scanned files by modified/created time (newest/oldest) or size (large/small), handy for grabbing the latest screenshots or targeting the biggest files
- Watch Mode — monitor a folder and auto-optimize new or changed images as they appear, with live SSE-streamed logs and rename detection
- Batch resume — progress is persisted per output folder; an interrupted batch (crash, cancel, server restart) can be resumed where it left off
- Pause / Resume / Cancel mid-batch, plus per-file compression parameter overrides and single-file pre-compression preview
- Before/after preview — overlay and side-by-side compare views
- Four compression modes:
standard— pngquant + oxipng, best size reductionlossless— oxipng only, no color lossresize only— scale down, no quantizationscreenshot— near-lossless pngquant pass tuned for UI/software screenshots (PNG-only, hardcoded dithering off)
- Output format — PNG (via pngquant/oxipng), WebP (via Pillow's built-in encoder, no extra binary needed), JPG (via mozjpeg's
cjpeg, keeping JPEG rather than converting), or AVIF (viaavifenc) - Keep-format JPEG optimization — select the JPG output format on a batch of
.jpgfiles and they're genuinely re-compressed lossily by mozjpeg while staying JPEG (requirescjpeginbin/, seebin/README.md) - EXIF retention (opt-in) — keep a curated EXIF subset (camera, date, exposure; GPS stripped for privacy)
- Color protection — list hex colors to preserve in the palette
- Dithering toggle — smoother gradients (standard mode)
- Results sorting & filtering — sort results by compression %, compressed size, or bytes saved; filter to show all/succeeded/failed
- Dark mode — manual theme toggle or auto-follow OS preference
- Internationalization — English and 中文 (Chinese) built-in, extensible to more languages
- SSE progress transport — real-time server-sent events for progress/logs with automatic polling fallback
- Output directory — save results to any folder (or use the built-in ZIP download), with a Reveal Output Folder button to jump straight to it in the OS file explorer
- Skip already optimized — re-running against the same output folder reuses existing outputs instead of recompressing unchanged files (settings-aware: changing quality/mode/format forces a real recompress)
- Recent folder history — quick-access chips with per-item removal and clear all
- In-app settings — gear icon opens a settings panel (host, port, workers, timeouts, language) persisted to
config.json - Session state restoration — page refresh restores files/results and re-attaches to a still-running batch
- Auto-detect binaries — finds
pngquant/oxipng/cjpeg/avifencinbin/folder or system PATH - Cross-platform — Windows, macOS, Linux (pre-built exe for Windows; macOS/Linux users need
pngquant/oxipng/cjpeg/avifencon PATH)
Download the latest release from the Releases page and run ImageOptimizer.exe.
Windows SmartScreen: The exe is unsigned (code signing certificates cost $200+/year for open-source projects). On first run, SmartScreen may show "Windows protected your PC" — click More info → Run anyway. This is normal for unsigned open-source software.
git clone https://github.com/aREversez/image-optimizer.git
cd image-optimizer
pip install -r requirements.txt
python -m app # or: start.batOpen http://127.0.0.1:8090 in your browser.
The first run will auto-detect pngquant and oxipng in the bin/ folder and system PATH. See bin/README.md if a binary is missing.
- Enter a folder path — type or browse to select a directory, then click Scan Folder (or drag & drop / upload individual files)
- Review files — deselect any you want to skip, sort by date/size, adjust quality/width/mode (per-file overrides via each card's gear icon, or click Preview for a single-file dry run)
- Protect colors (optional) — add hex colors like
#2ecc71to preserve - Start — click Start and watch the live progress log (SSE-streamed)
- Compare & download — click any result's Compare button for before/after preview, then download the ZIP or click Reveal Output Folder (when an output folder is set). Sort/filter results by compression ratio or size.
Alternatively, set up Watch Mode (sidebar card) to auto-compress new images dropped into a folder, and use Resume on page load if a previous batch was interrupted.
Command-line flags:
python -m app --host 127.0.0.1 --port 8090 --workers 4 --dir "D:\screenshots"
--host— listen address (default127.0.0.1, see Security below)--port— listen port (default8090; if taken, the next free port is used automatically)--workers— how many images to compress concurrently (default4). Lower this if pngquant/oxipng are already maxing out your CPU; raise it on a many-core machine for faster batches.--thumbnail-workers— how many images to thumbnail concurrently during a scan (default4). Independent from--workers, so a scan's I/O-bound thumbnailing can be tuned separately from CPU-bound compression.--dir— auto-scan this folder on startup--recursive/--no-recursive— when used with--dir, whether to scan subfolders too (default--recursive, the historical behavior). Pass--no-recursiveto scan only the top level.
For settings you don't want to type every time, either use the in-app
settings panel (gear icon in the header — changes persist to
config.json; host/port/workers changes require a restart) or create a
config.json by hand in ~/.image-optimizer/ (that's
%USERPROFILE%\.image-optimizer\config.json on Windows, or
~/.image-optimizer/config.json on macOS/Linux — the same folder on every
OS, and the same one recent.json already lives in). Copy
config.example.json there and edit it, or start
from scratch with:
{
"host": "127.0.0.1",
"port": 8090,
"concurrent_workers": 4,
"thumbnail_workers": 4,
"workspace_cleanup_delay": 10.0,
"session_idle_timeout_hours": 4
}Other flags:
workspace_cleanup_delay— seconds to wait before deleting a workspace after it's replaced by a new scan/upload (default10.0). Files aren't deleted immediately so an in-flight request — e.g. a ZIP download or an image still loading in another tab — has time to finish before they disappear out from under it. Raise this if you routinely have slow downloads running when you start a new scan; lower it to free disk space sooner.session_idle_timeout_hours— how long a browser session (and its scan results, in-progress state, and temp workspace) is kept after you stop using it before being cleaned up automatically (default4). Each browser tab/session is tracked separately so multiple tabs don't interfere with each other; this controls how long an abandoned one hangs around before its temp files are freed. Also sets how long the session cookie itself is valid for.
All keys are optional — only include the ones you want to change from the
default (an empty {} or a missing file both just mean "use every
default"). Command-line flags always override config.json, which
overrides the built-in defaults. Invalid values (e.g. a negative worker
count) are ignored with a startup warning rather than crashing.
This app has no authentication and is meant to run on 127.0.0.1 only (the
default). It can read any directory you point it at and write to any path
you specify as the output folder — fine for local, single-user use, but
don't start it with --host 0.0.0.0 or a LAN address unless you fully
trust everyone on that network, since there's nothing stopping them from
reading or writing files on your machine through it.
- Source install: Python 3.10+ with
fastapi,uvicorn,Pillow,jinja2,python-multipart(pip install -r requirements.txt, orpip install -r requirements.lockfor the exact versions this was tested against) - PNG compression:
pngquantandoxipng(PNG-only modes; JPG/WebP output and lossless fallbacks don't need them) - JPG output:
cjpegfrom mozjpeg (keeps JPEG format instead of converting; seebin/README.md) - AVIF output:
avifencfrom libavif (optional — without it the AVIF format simply isn't offered; seebin/README.md) - Standalone EXE: Windows 10/11 64-bit, no Python required (all four encoders bundled)
image-optimizer/
├── app/ # Python web application
│ ├── main.py # FastAPI server & routes
│ ├── optimizer.py # pngquant/oxipng/cjpeg/avifenc orchestration
│ ├── watcher.py # Watch Mode directory poller
│ ├── models.py # Pydantic request models
│ └── templates/ # HTML/CSS/JS frontend (single-page app with i18n)
├── assets/ # Logo, icons, banner images
├── bin/ # Binary dependencies (pngquant, oxipng, cjpeg, avifenc)
├── tests/ # pytest test suite (249 tests)
├── build_exe.py # PyInstaller build script (onefile mode)
├── pyproject.toml # pip-installable package config
├── requirements.txt
└── start.bat
MIT