Building the Image Grid from Google Photos

Engineered for fast image loading and smooth scrolling

That was a great milkshake

First: Why the easy solutions don’t work

The most straightforward way to display a collection of images on the web is to show the images in pages of rows and columns, with 3–5 images per row and 20–50 images to a page. It’s tried and true, but not a great solution for my purposes. If I post 1,800 photos in 36 pages, you can be sure that very few people are going to make it past page two or three.

Finding an elegant solution

I wanted to design an image grid that displays all two thousand images on one page, without showing more than 50 or so images at a time. To do this, I would need to both load and unload images onto the page, to ensure that a reasonable number of images were on screen at once.

Enter Google Photos

I figured out a solution to this problem while scrolling through my food pictures on Google Photos. The web app choreographs a similar infinite-scroll that I wanted to build, while also allowing images to shift around smartly as you resize the page.

Using translate3d for layout

The basic layout consists of a bunch of <figure class=”pig-figure”> elements inside a container, with the following CSS:

  1. Loading and unloading: Because each <figure> element is absolutely positioned, we can safely remove and add new elements without affecting the position of the others.
  2. Responsive sizing and layout: When you resize the window, you can update the width, height, and translate3d properties in the inline styles, and shift images around using CSS transforms.

How it works

Reverse engineering the Google Photos image grid proved complicated. The grid consists of many rows of images, where each row has the same spacing between images and is perfectly sized to fill the container, but each row might have a different number of images, or a different height. Importantly, within each row, all of the images are the same height.

Making it fast

My original implementation involved running this entire algorithm every time a scroll event fired, which left me with some serious lag. In order to achieve smooth scroll behavior, I removed as much work from the scroll handler as I could.

  1. _computeLayout is responsible for creating a huge list of objects, one per image, where each object contains a reference to a DOM element, as well as it’s desired height, width, translateX, and translateY values. I only call this on load and resize, when the layout needs updating.
  2. _doLayout takes the latest scroll position and direction, determines the primary and secondary image buffers, and then uses the data structure created in _computeLayout to appropriately hide, show, and position the DOM elements.

Making it (even) faster

The next step was to make sure that the grid ran well on mobile devices as well as desktops. To do this, I looked at how Medium loads images in their articles, and brought that to my grid.

Medium’s progressive blur/focus loading, implemented using CSS filter:blur()
Progressive image loading in action

Recap: Performance-first design

I believe that on the web, performance can be one of the biggest components of good design and storytelling. With pig.js, I put speed as my one and only concern for the library. It was a lengthly process, but I ended up with four core tactics for making the grid smooth:

  1. Dynamically load and unload images above and below the viewport to reduce the number of elements on the page at once.
  2. Make the primary and secondary image buffer different sizes, because we are more likely to load images in the direction we were already scrolling.
  3. Separating out layout computation and DOM manipulation into two different functions: _computeLayout and _doLayout, only running the former on load and resize, drastically reducing the work we have to do on scroll.
  4. Implement a responsive version of Medium’s progressive image loading, making on-the-fly decisions for what size image to load.

The end result? It’s pretty freaking fast

If you don’t believe me, head over to my demo page. If you want to take it for a spin, fork or clone me on GitHub.

--

--

cofounder, @ambrookag. previously product @nytimes, @googledrive, @firebase, engineering @thenext50us, @minimill_co. https://schlosser.io

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Dan Schlosser

cofounder, @ambrookag. previously product @nytimes, @googledrive, @firebase, engineering @thenext50us, @minimill_co. https://schlosser.io