Dark mode works well until a page contains images. A black-on-white diagram may look better inverted. A photo usually will not.
A website can leave every image alone, invert every image, or tag them one by one. None of these options works well at scale.
Simple rules also fail. A white background does not mean an image is safe to invert. It may contain subtle colors, gradients, or a photo inside a diagram.
A small image classifier
I found the idea on Gwern’s idea list and built InvertOrNot to try it.
I fine-tuned EfficientNet-B0 on a custom set of labeled images. The API runs the exported ONNX model on a CPU, so deployment stays simple and does not need a GPU.
The API
The API accepts uploaded files, image URLs, or SHA-1 hashes. Each response includes the decision, the image hash, and any error.
Redis stores predictions by hash. For URL requests, it also remembers the URL’s last hash. Repeated requests can then skip the model, although a URL that changes its content can leave a stale mapping.
Learning from corrections
When a result is wrong, a user can submit a correction. The API adds the corrected label and image URL to a Redis queue. A separate script downloads those images into a dataset that I can use to fine-tune the next model.
This is not automatic retraining. It is a simple way to collect the real edge cases that matter.
Limits
Many cases are clear: diagrams often benefit from inversion, while photos do not. Mixed content and unusual colors are harder, and some images are genuinely ambiguous.
The model returns a binary decision using a 0.5 threshold. That makes the API easy to use, but it hides how close a prediction was to the boundary.
Deployment
The service uses FastAPI, Redis, and ONNX Runtime. It runs on one server without a GPU. CORS is open, so any website can call it.
The frontend is just as small: drop in an image or try an example to see the result.
A build-time integration pattern
A static site could call InvertOrNot at build time instead of classifying images in the browser. A build script would hash each image, check a local cache, and call the API only for new hashes.
Keeping the cache in version control makes later builds repeatable and avoids extra API calls. A manual override can handle the few decisions that do not fit the site’s design.
This site now uses that build flow. A Node.js script scans the article sources, hashes each local image, and reuses predictions from a cache committed with the site. It sends only new hashes to InvertOrNot, generates dark-mode CSS for the article images, and accepts manual overrides for incorrect decisions. Paintings, project artwork, favicons, and Open Graph images stay outside the classifier.
Simple rules cannot cover every image. A small classifier gives me a useful default, and the correction queue gives me better data for the next version.
Try it at invertornot.com or read the code on GitHub.
SQLGBM: Running ML models directly in SQLinlineCMS: Edit Markdown in the browser
Comments
Nothing yet. Say the first thing.
Sign in to join the conversation.