TL;DR
Base64 turns every 3 bytes into 4 characters, about 33% bigger. On a gzipped response an image's penalty shrinks to about 1% (1.010x of the original in our test), but an uncompressed response pays the full third.
Rule of thumb: inline only an icon of a few KB that your own code owns and uses once. Serve everything else as a file, because an inlined image re-downloads with every revision of the file that holds it.
In the browser use FileReader.readAsDataURL . In Python use base64.b64encode(...).decode("ascii") . btoa throws on any character outside Latin-1.
A reviewer writes "just base64 it", and the image moves into the file that holds it. It can no longer be cached on its own, it comes back with every deploy of that page, and it is a third bigger before compression.…