The 400 KB hero
12 KB saved, 2 MB I never opened. A retina decision I got wrong in both directions.
I spent most of a day in September getting a stylesheet from 96 KB down to 84 KB. I concatenated things. I stripped comments. I found a chunk of dead CSS from a section that got cut and deleted it, which felt great.
12 KB! And then I ran the page through a waterfall for the first time in weeks and the homepage was 2.3 MB, of which about 1.9 was 4 images.
I want to be clear that I knew, abstractly, that images are most of the weight of most pages. Everybody knows that. It’s in every talk. And I had still spent a day on 12 KB, because the CSS was the part I felt ownership over and the images were something that arrived from someone else.
Let sleeping images lie
The thing that actually surprised me, when I finally went and looked, is how little sophistication was required. I didn’t need a better format or a clever technique. I needed to open the JPEG and look at it.
The hero was 2400px wide because that’s what came out of the design tool at the size the comp was built. It was displayed at a maximum of 1200. So half of every dimension, which is 3/4 of the pixels, was being downloaded to be thrown away. Resizing it to what it was actually displayed at took the file from just over 1 MB to about 280 KB, and I could not tell the difference on any of my devices.
Then quality. It had come out at whatever the default export was, which is somewhere in the 90s, and JPEG quality has sharply diminishing returns past about 75 on lifestyle photos. Going to 75 took it to 140 KB. I put the two versions side by side at full size and stared for a while and genuinely could not pick them apart, and I was trying to.
I didn't need a better format or a clever technique. I needed to open the file and look at it.
So: a megabyte down to 140 KB, on a single image, using 2 settings that have existed for 20 years. Versus my 12 KB of CSS. That ratio is the whole post really.
Metadata
The other easy one. Photos out of a camera carry EXIF, and photos out of an editor carry an embedded color profile and sometimes a thumbnail and occasionally a full copy of the editing history. On one of the 4 images that was 60 KB of the file, and it is doing absolutely nothing for anybody visiting the site.
Run them through a lossless optimizer as a build step and it strips all of it without touching a pixel. It’s a one-liner in Grunt or Gulp, or a drag-and-drop app if you’d rather, and I now put it in every project on the first day so I never have to think about it again.
What even is retina?
This is the part I’m least confident about and the part that’s changing fastest so take it accordingly.
My first instinct was to serve everything at 2x so it’d look sharp on the newer phones and laptops, which doubles the dimensions, so quadruples the pixels, and I was doing that to people on phones, on mobile networks, to fix a problem they mostly didn’t have.
Then I overcorrected and served everything at 1x, and the site looked unmistakably soft on those sweet, sweet retina devices.
Where I’ve ended up, and I hold this loosely: for photographs, a 2x image at a much lower JPEG quality is often smaller than a 1x image at high quality and looks better on both kinds of screen. The compression artifacts get scaled down along with everything else, so you can be much more aggressive than feels comfortable. Something like quality 75 at 2x. It looks meh in the file browser but fine on the page. That trick doesn’t work for flat graphics or anything with text in it, where the artifacts are very visible, and for those I’d be reaching for SVG anyway.
The proper answer is srcset, so the browser picks based on what it knows about the screen and the viewport which is more than the server knows. It landed in Chrome earlier this year and there’s a polyfill for the rest, and I’ve used it on one thing. I’d like to say I’ve adopted it but I haven’t yet, mostly because the markup is verbose enough that it’s a conversation with whoever’s maintaining the CMS templates. I suspect this is the thing I’ll look back on as obviously worth doing sooner.
<img src="hero-1200.jpg"
srcset="hero-1200.jpg 1200w, hero-2400.jpg 2400w"
sizes="(min-width: 60em) 1200px, 100vw"
alt="Sunrise over the harbor">
Don’t blame me, blame the process
Here’s what I think is really going on:
The images don’t come from me. They come from a designer, or from the client’s marketing folder, or from a stock library, and they arrive at whatever size they happened to be. And then they go into a CMS, where the client will upload more of them for the next three years, at whatever size those happen to be.
So any fix I make by hand is a fix with a half-life. I can optimize the four launch images beautifully and it’ll be undone by the next content refresh.
Which means the only version of this that survives is the one in the pipeline. Resizing on upload, a quality setting, the optimizer in the build. Boring infrastructure, done once, that keeps working while nobody is paying attention. That’s the actual deliverable, and it’s much less satisfying than the afternoon where I made a photograph 88% smaller and felt like a genius.
I need to start giving a damn
My honest takeaway from the whole thing is that I optimize what I have opinions about. I have opinions about CSS but I did not have opinions about a hero image. So the image went unexamined for four months while I sharpened something that was already fine.
I’d guess that generalizes. The unexamined part of any project is not the hard part; it’s the part that belongs to somebody else, and it stays unexamined precisely because everybody assumes it’s handled.
So, small suggestion to this post: open the network tab on something you launched recently and sort by size. Don’t optimize anything, just look. My guess is the top of that list will not be code.