Indak Media Optimizer releasesself-updates
WordPress plugin: converts images to lean WebP in the browser before they reach the Media Library
by Indak Media · github.com/indak-media/indak-media-optimizer · website
Install
The author publishes release zips, so WP-CLI can install straight from GitHub:
wp plugin install https://github.com/indak-media/indak-media-optimizer/releases/download/v0.4.0/indak-media-optimizer.zipShips its own WordPress updater (built-in updater), so new versions show up under Dashboard → Updates.
Readme
Indak Media Optimizer
WordPress plugin that converts images to lean WebP in the browser before they reach the Media Library. Only the web-ready file is ever uploaded, so the server never stores the 6 MB camera original.
No settings screen. Drop a JPEG/PNG anywhere WordPress accepts uploads and it arrives as a WebP, capped at 2560 px, at the lowest quality that still looks identical to the source.
What it does
| Step | Where | Detail |
|---|---|---|
| Intercept | Browser | Hooks both upload paths WordPress uses: Plupload (Media Library, Add New, classic editor) and REST/apiFetch (block editor drops). |
| Resize | Browser (worker) | Longest edge capped at 2560 px (WordPress' own big_image_size_threshold). Never upscales. EXIF rotation honoured. |
| Encode | Browser (worker) | libwebp (WASM) searches for the lowest quality between 50 and 90 whose SSIM against the resized source is ≥ 0.93. ~2–6 encodes per image. |
| Upload | Browser → WP | The WebP replaces the original in the upload; WordPress builds thumbnails from it as usual. |
| Record | PHP | Original size/name stored as attachment meta; "Saved 84% (3.2 MB → 510 KB)" shown in attachment details and the Media list view. |
Skipped (uploaded untouched): SVG, GIF, existing WebP/AVIF, video, PDF, anything the browser can't decode (HEIC in Chrome, CMYK JPEG), and any conversion that would save less than 10 %.
The single control is a checkbox under every drop zone: Upload originals (skip WebP optimization). It's remembered per browser tab only.
Images that didn't come through the uploader
Divi imports, FTP drops and site migrations write files straight into
uploads/ and never touch the browser, so they skip the pipeline. For those
there is Optimize now:
- Media Library list view: an "Optimize now" row action on every eligible image, and "Optimize now" in the Bulk actions menu.
- Attachment details (media modal, grid sidebar, Edit Media): an "Optimize now" button.
It runs the same in-browser pipeline on the existing file and swaps it in
place: same attachment ID, same title/alt/caption, no new library entry.
The server writes the .webp next to the old file, regenerates thumbnails,
rewrites every reference to the old URLs (posts and revisions, postmeta,
options — plain, serialized and JSON-escaped forms) and then deletes the old
original and its thumbnails. Divi pages keep working because their image URLs
now point at the WebP files.
Optimize all
For a migrated site with hundreds of images, per-page bulk selection is slow. Optimize all — on the Dashboard widget and beside "Add Media File" in the Media Library — asks the server for every image not optimized yet and runs them 2 at a time in your browser with a progress bar, running totals and a Stop button. Images that turn out to be already lean are remembered and drop out of the queue. Keep the tab open; a stop takes effect after the current image, never mid-swap.
Dashboard widget
The wp-admin Dashboard gets a Media Optimizer box: images optimized, space saved, average reduction, and how many images have not been optimized yet (with a link to the list view). Cached for an hour; refreshed on every optimization or deletion.
Install
- Download the latest zip from Releases
(or copy this folder to
wp-content/plugins/indak-media-optimizer/— the builtassets/folder is committed, so no build step is needed either way). - Activate Indak Media Optimizer in Plugins.
- Upload an image. Open the browser console to see the numbers per file.
Requires WordPress 6.5+, PHP 8.0+, and a server whose image library can handle WebP (GD or Imagick with WebP support — standard on Local, Hostinger, Kinsta, WP Engine). If WordPress reports it can't edit WebP, the plugin automatically uploads originals instead of failing.
Updates
Sites running this plugin get update notices in wp-admin like any
wordpress.org plugin, via Plugin Update Checker
(includes/Updater.php), which polls this repo's GitHub Releases.
To ship a release: bump Version: in indak-media-optimizer.php and
CHANGELOG.md, commit, then tag and push:
git tag v0.2.0
git push origin v0.2.0
.github/workflows/release.yml builds assets/ and publishes a clean zip
(no test/, docs/, src/, tooling) as the release asset that sites
download.
Develop
npm install
npm run dev # rebuild assets/ on every change
npm run build # one-off production build
npm test # Playwright tests against the built assets (see docs/TESTING.md)
Commit assets/ after building — it's what ships.
Tuning (developers only)
Override defaults from a theme or mu-plugin:
add_filter( 'indak_media_optimizer_config', function ( $config ) {
$config['maxDimension'] = 1920; // px, longest edge
$config['ssimTarget'] = 0.97; // stricter = larger files
return $config;
} );
All keys: maxDimension, ssimTarget, qualityMin, qualityMax,
convertTypes. See includes/Config.php for what each does and
docs/DECISIONS.md for why the defaults are what they are.
Layout
indak-media-optimizer.php Plugin bootstrap
includes/ PHP: Assets (enqueue), AttachmentMeta (savings), Config (filter),
RestApi + Replace + OptimizeNowUi ("Optimize now"),
Stats + DashboardWidget, Updater (GitHub Releases)
.github/workflows/ release.yml — builds + publishes the update zip on `vX.Y.Z` tags
src/ JS source (built by Vite into assets/)
Main.js Entry: installs interceptors + toggle
Uploader/ InterceptPlupload, InterceptApiFetch, SkipToggle
Pipeline/ OptimizeFile (main-thread orchestration), Eligibility
OptimizeNow/ OptimizeExisting (fetch → optimize → REST replace), AdminUi (buttons, bulk),
BatchRunner (shared queue), OptimizeAll (whole-library run)
Compress/ Worker, Pool, Raster, Ssim, QualitySearch (from SlimPix)
assets/ Built output — committed
test/ Playwright harnesses, specs, fixtures, calibrate.mjs
docs/ ARCHITECTURE, DECISIONS, TESTING
Credits
The compression engine (src/Compress/) is lifted from Indak's SlimPix
project. WebP encoding by @jsquash/webp
(libwebp compiled to WASM).
Read the full README on GitHub →