How I Made Pixels ⚡️ Fast

Pixels is a collection of kick-ass designs updated every day. It's built on the same technology I made for Klart.co, a super friendly (🙊) bookmarking service for designers. After Pixels took off as a consequence of a blogpost I wrote earlier, I've got some more feedback on it.

Previews

First, a lot of people asked me about how I can serve those blurred previews of all designs and how they can be so accurate. What I do is that I convert the original images into a 4x4 GIFs that I in-line with the JSON data. You can read more about it in an earlier post here.

Big, very big, images

Another thing that appeared to be an issue on slower connections is that, while previews are loaded fast, the smaller images in the grid view are not. Why? Well, they're actually the originals. Of course I don't load hundreds of original, full resolution PNG images at once. But instead, I lazy load them when they're in your viewport. One of the nice things with this is that when you click the image to open the modal, the image will be cached in full resolution so you don't have to load it again. There are still room for improvement though, since the files are large and look weird on non-retina displays when downsized too much.

Optimizing for common case

It's great that all images are already fetched when you open the modal to make an image larger. But I've noticed that the case is often that a user scrolls through loads of images before clicking any. And since I want to make users happy, I have to optimize for this 😊.

Building an image thumbnail service

I decided to make a thumbnail service that will serve a smaller version of the original in the grid view. It works as following:

Request /:id/thumb.png
  Find Pixel with ID
  Return 404 if not found

  Send file thumbs/<id>.png
  If no such file. Create it from original.
  Send newly created file.
End

Now, I can always just change the filenames to support file sizes, quality, filters etc. For example, I could use a filename like <id>.WIDTH.HEIGHT.png to support different sizes.

For the implementation I use Node and Sharp (an awesome, fast and open source image manipulation library). I know that I could use Amazon S3 and Imgix, Cloudinary or some other service to do this. But I'm bootstrapping and don't have a big bag of money + I want to learn how to do this myself.

Determining the size of thumbnails was not super scientific. I found that a width of 800px works great on both retina and non-retina displays (that's about twice the size of the containers).

How I serve an image now

To sum things up, this is how images are served on Pixels now.

On start page
1. Render GIF inlined in JSON response and start fetching thumbnail
2. When thumnail is ready, render it
3. When modal is opened, display inlined GIF preview and fetch original (future work 🤔?) 

Future work

I'm looking forward to see the results of this. I might add background loading of originals, especially the next 2/3 images when you're browsing in the modal. Maybe even load the first 6 originals or something. I might also look into more precise sizing of the thumbnails. If you got some ideas, don't hesitate to send me an email or tweet @drikerf 😊.

Lessons learned

I've learned a bunch of stuff about serving images and building things

  • You'll get away with a lot of things in the beginning. Cut corners and get whatever you're making out of the door.
  • Listen to users and see how they use your product
  • PNG images can be very big 🙈
  • Performance is important later on but doesn't have to cost a lot of money