
Ask HN: How to display a large number of images speedily? - dhruvkar
I&#x27;m trying to load about 700 to 1000 images on a page. I&#x27;m a newbie when it comes to front-end dev. Here&#x27;s what I&#x27;ve done so far:<p>* Optimized the images (~250 kb&#x2F;image)
* created thumbnail versions (&amp; load full versions in a modal)
* added lazy loading<p>It&#x27;s deployed on netlify, but it&#x27;s still not quite snappy.
If possible, I&#x27;d like to avoid pagination.<p>What else can be done to load images faster?
======
jepler
Are you lazy-loading your (thumbnail) images? this js library seems to be
highly recommended by its author:
[https://github.com/ApoorvSaxena/lozad.js](https://github.com/ApoorvSaxena/lozad.js)
but I don't have any experience with it.

If users typically view the full images sequentially, then a lightbox that
preloads the next image(s) may help with perceived performance.

~~~
dhruvkar
thanks, I'll check out that library. I'm using blazy
([https://github.com/dinbror/blazy](https://github.com/dinbror/blazy)) right
now.

------
PaulHoule
The first problem you have is that this is a lot of data. You are talking 250
MB or so worth of images. If you use JPEG, you can get images very small at
the cost of image quality. There are also tools like PNG Gauntlet that will
reduce PNG sizes; one thing that tool will not do is convert an RGB image to a
limited palette and then store it as indexed. Sometimes that works wonders.
Both Google and Microsoft have image formats that compress better than PNG,
but they don't support each other's formats.

In your case I would consider some kind of "infinite scroll" gimmick like
Twitter. Maybe you load the first 5 images and as the user scrolls down you
load more.

~~~
dhruvkar
yeah, that's what the lazy load plugin is doing right now.

does that mean pngs are preferable to jpegs?

------
wanda
Lazyloading is good, I've written a pretty speedy lazyloader myself that also
works for CSS background images, but serving up lo-fi thumbnails when many
images are displayed at once is something I tend to do regardless.

Lazyloading often works by checking if an image element is in the viewport —
if so, the _src_ attribute is added dynamically and the image is loaded as
soon as this is complete.

However, if the user scrolls quickly or has a large screen, lots of images may
be loaded simultaneously — and if those images are all fairly large, this may
cause some performance problems.

You could try using _requestAnimationFrame_ to load images one-by-one, but at
the fastest possible rate, i.e. to stagger the loading of images. You could do
this by listening for the 'load' event on each image in order to trigger the
loading of the _next_ image.

Assuming you store the image URL in a data attribute like 'data-src', a
simple, vanilla implementation of what I just described might look like this:

    
    
        /* 
          In production, we would throttle or debounce this scroll handler, 
          but for brevity we'll just attach it directly:
        */
        window.addEventListener('scroll', function () {
          
          var all_imgs = document.querySelectorAll('img[data-src]');
    
          // [].slice.call to convert qSA's NodeList to an Array
          var visible_imgs = [].slice.call(all_imgs).filter(function (img, index, arr) {
            /* 
              your logic for determining if the image is in the viewport would go here
            */  
          });      
    
          return loadImages(visible_imgs);
        }, false);    
    
    
        /*
          This function will work by being passed an array of images
          that were in the viewport at the time of the scroll event
          triggering, and will call itself recursively until all 
          of those images are loaded.
        */
        function loadImages (imgs) {
    
          if (imgs.length < 1) { 
            return;
          }
    
          return requestAnimationFrame(function () {
            
            imgs[0].src = imgs[0].getAttribute('data-src');
            
            // remove the data attribute so that the image isn't loaded over and over again
            imgs[0].removeAttribute('data-src');
    
            imgs[0].addEventListener('load', function () { 
              return loadImages(imgs.slice(1)); 
            }, false);
    
            imgs[0].addEventListener('error', function () {
              return loadImages(imgs.slice(1)); 
            }, false);
          });
        }
    
    

NB: I have not really tested this approach or the code above, I'm shooting
from the hip here because I'm in transit right now — but if it does work, it
might help improve _perceived_ performance. Or it might make everything look
even slower in which case I'm an idiot.

------
arkitaip
All those http requests cost so maybe combine the thumbnails into a few
sprites consisting of 64x64 graphics?

~~~
dhruvkar
would you combine, then split it in the browser? is there an example?

interesting, I've never heard of this approach.

~~~
arkitaip
You have one huge image and then use css to "extract" the graphics as needed,
please see [https://css-tricks.com/css-sprites/](https://css-tricks.com/css-
sprites/)

~~~
dhruvkar
ah got it, thanks

