◉ANTIGRAVITY LABJP
Articles/App Development
▣ App Development/2026-07-13Intermediate

The Gate That Stops Visual Damage Before You Hand Bulk Image Optimization to an Agent

When an agent bulk re-encoded a few hundred wallpaper assets, a handful came back with dulled color. Size-reduction alone cannot catch that. Here is how to design a gate that stops bad conversions before merge using three axes — SSIM, ΔE, and file size — with a checker that runs on Pillow and scikit-image.

antigravity464app-dev54imageautomation95quality-gate3

✦ Premium Article

One night I was tidying the assets in one of my wallpaper apps. There were about 320 preview PNGs for the home grid, and I asked an agent to batch-convert them to WebP. By morning the log looked flawless: total size down from 41MB to 17MB. Nothing to argue with.

Then I checked on a real device. A few images — deep indigo night skies — had turned slightly gray. The kind of difference you only notice when you place the two side by side. The agent had reported "size reduced" as success, and I had trusted that report right up to the edge of merging.

Something tightened in my chest. Had it shipped unnoticed, the color of the very pieces I most wanted people to see would have been quietly degraded.

This article is about how to design a gate that captures "visual damage" as numbers and stops it before merge when you delegate bulk image optimization to an agent. I will share the implementation from an indie wallpaper app — a place where color itself is the product — along with the threshold guidance I settled on in production.

Why "size went down = success" is the wrong place to stop

Image optimizers love to report their reduction ratio, and agents do the same: "Converted 320 images, average 59% smaller." That is not a lie. But a reduction ratio only tells you how much was thrown away, never what was thrown away.

Most of the loss is invisible. Lossy JPEG and WebP compression, depending on the quantization tables, introduces banding across smooth gradients. Conversions that reduce the palette round off subtle tonal steps. If a color profile is stripped, pixels with identical values render as different colors.

Each of these produces a "perfectly valid file" that sails past corruption checks. Human review has limits too. Comparing 320 images one by one against the originals exceeds the number of frames your attention can actually hold. That is precisely the kind of quiet degradation I nearly missed.

So we need to translate degradation into numbers a machine can measure, and route only the ones that fall below threshold back to a human. Let the agent do the optimization itself — that is fine. What it must not own is the final pass/fail decision.

Capturing visual damage as numbers — three axes

I judge on three axes. One is not enough; the three cover each other's blind spots.

AxisWhat it measuresWhat it catches
SSIM (structural similarity)Agreement of luminance, contrast, and structureBanding, crushed detail, softened edges
ΔE (color difference)Perceptual color shift (distance in CIELAB)Hue drift, desaturation, profile stripping
File sizeThe effect of optimizationBoth over-compression and no-op conversion

SSIM is 1.0 for a perfect match, and lower as structure breaks down. Unlike a per-pixel difference (MSE), it captures structural change in a way closer to whether a human would call two images "the same." Its sensitivity to banding and crushed detail is the advantage.

SSIM, however, is relatively blind to hue drift. As long as luminance holds, a blue leaning slightly green barely moves the score. For a wallpaper app, where color is everything, ΔE fills that gap. ΔE is a distance in CIELAB space, where roughly 1.0 is "a difference a trained eye can just perceive," and 2–3 is "a difference visible when placed side by side."

The third axis, file size, checks the health of the optimization rather than degradation itself. An extreme reduction ratio hints at over-compression; almost no reduction means the conversion is not doing anything — so there is no reason to take on a quality risk for it.

✦

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
✦A gate design that catches the quality loss a size-reduction number can never reveal, using SSIM, ΔE, and file size together
✦A CI-ready image quality checker built with nothing but Pillow and scikit-image
✦Real threshold guidance by conversion preset, drawn from running a wallpaper app in production
Secure payment via Stripe · Cancel anytime
✦

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Antigravity Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • ✦Copy-paste ready implementation code
  • ✦New advanced guides published daily
  • ✦$5/mo or $10 for lifetime access
View Membership →

Related Articles

▣ App Dev2026-10-08
Three Safety Nets to Test with a Fake agy Before Running the Antigravity CLI in a Container
Before you call the Antigravity CLI (agy) from Docker or a Cloud Run job, reproduce three failure modes with a fake agy: an installer that returns gzip, a hang on tool approval, and an exit code of 0 with nothing produced. Everything here rests on official docs and on code I ran myself.
▣ App Dev2026-09-10
When the iOS 27 Date Landed, the First File I Grepped Was AGENTS.md
iOS 27 and iPadOS 27 ship on September 14. Old version numbers linger in your source, but also in the files only your agent reads. Here is a script that counts both zones, plus the one line keyword search will never find.
▣ App Dev2026-09-04
The Five Days Between the iOS 27 RC and Release — I Check Init Order and Entitlement Restore Before Layout
A record of how I narrow the agent's search scope during the short window before an OS release — by launch path rather than by screen. Includes the audit script I run to catch consent and ads init ordering, entitlement restore gaps, and hardcoded size branches.