A simple photo gallery
The size of our photo collections is growing rapidly on a day-by-day basis and it becomes increasingly harder to organize the pieces in them so that we can find them later. Proper labels and metadata help here, but we often don't have the time to change them for each individual file separately. The result is that when we need the data, we don't know by which keywords to look for it.
Our photo collection is a window to our world since it resembles the unique viewpoint that we had by the time we pressed the button. Framing the photo is a unique skill by itself, especially when we have to divide the scene through objects of different sizes having them in certain proportions as to introduce some dynamism in the scene. Taking macro and landscape photos require different approaches and lenses. Sometimes a photo isn't even the reality, but a scene that we carefully composed out of the materials available to us.
It probably doesn't make sense to try to make all our photos public, because most of them will probably not resemble our best work. But choosing the best ones (the needles in the haystack) may require a lot of time. Still, if we can narrow down our choice, our chance to create a beautiful gallery will increase. The idea behind sampling is similar.
What would be the optimal amount of images to show? We probably can't include hundred images in our gallery and expect that they will attract sufficient attention. The size of the screen area will also affect what will be visible by default. The network will limit how many images we can show to our users, especially when they browse our gallery on a mobile phone. Another consideration is the size of the images themselves. If we choose large default images, then we will need to scale them down, so they can fit on smaller screens. Scaling is relatively expensive operation and should be avoided when possible. In itself, it can't reduce the file size unless the smaller version is saved so we can reference it later. In the past we had web sites that loaded scaled down versions of big photos on mobile devices, which slowed them down. We could also have our photos in formats that despite of being more efficient, aren't universally recognized by all browsers, so there is a risk that the browser may not load the file, load another format or even load different, but same value resources more than once.
It is important that at least the main subject in our photo has size that makes it clearly distinguishable. Showing photos that are too small for it to be seen contributes to screen space waste. This means that a photo can be too big (which results in slower loading and more janky scrolling) or too small (which results in having no more information than we previously had, yet inability to use the space for another purpose). So, if possible, we need to check that our photos don't have extreme sizes.
A good gallery can have an interesting layout that emphasizes some images and suppresses others. This can be done through image sizes alone. But it introduces a computational problem, especially when neither the width nor the height of the images is fixed. Now we have to deal with special cases. We need to remember the regions where we place images and also keep track of which were already used. When we try to place a new photo on the screen and one of its corners lies within the boundaries of another image, we have to check in which direction to move the new image so it doesn't overlap. We can move it to the right until this corner goes otside the boundary and then immediately to the top until it reaches the bottom boundary of another image or we can move it to the bottom until it goes again outside the boundary and then immediately to the left until it reaches the right side of another image. This needs to be done in a way that minimizes the total waste area (the space remaining between the rectangles where no new ones would fit). Given that we don't have an infinite space to ensure sufficient distance between the rectangles without worrying about their boundaries, this becomes a computationally intensive task. What I have just described is the rectangle packing problem, which has been shown to be NP-complete (not solvable within a reasonable amount of time).
Since packing our photos in various sizes would be very hard, we can either fix the width or the height and now placing all photos next to each other becomes much easier. (Take a moment to look at Google Images to see how they approach the problem.) Moreover, we can reduce the unoccupied space and achieve a grid-like structure, perhaps even without a separate grid framework.
There is a trade-off in being users of our own gallery though. We can choose to crop our photos to the appropriate dimensions needed for the gallery and then batch-optimize them all at once if they are in a single directory (RIOT is relatively good at this). This will achieve great results in terms of optimization, but it will cost us more time. We need to be able to create our software in a way that requires minimum intervention on our side and ideally, works without supervision (you probably remember unsupervised learning). Loading a graphics editor for each newly available image doesn't sound like a good idea considering how slow we can be as operators.
Fortunately, we can let the users request the photos, then check whether we have a thumbnail version corresponding to the image they want to see. If not, we generate it automatically on the server (which takes some time, but only once), if yes – we just have to load in the browser. This way we can provide the content as it is requested, without having to store one that hasn't proved to be interesting. If we want to speed up the generation process, we can choose to rescale the images (preserve the aspect ratio) instead of resampling them (preserve quality as much as possible). The first will produce images that are slightly pixelated, but relatively close to the original (fast). For the purposes of the gallery presented here, I have chosen resampling, as I don't show too many photos. But even with a large gallery, we could still use resampling. If people request the images at random, initially the gallery would be very slow, since no thumbnails are generated. The more photos are requested, the lower the probability becomes that some of them won't have a pre-generated smaller version. This means that the gallery becomes faster the more it is used (which is good in many cases). At a certain point, when only 1-2 new images need to be generated, the user may not even notice the work in the background. If user A requests an image that user B wants to see later, B would be able to load the file without waiting. We also have the advantage of being able to store original photos in the directory and forget about manually creating thumbnails. We won't have the most optimal file sizes this way, but at least we have saved us some time.
If we had more images, we could remember the ones that the user saw previously to ensure that they won't come up again. This way we could still show a limited number and have a fast page, while knowing the position of the user in the whole continuum. Once the final screen has been reached, we can delete this information, so that the first images in the gallery appear again. This allows us to avoid the use of distracting interface elements. Now we don't have a concept of previous and next pages; the “next page” is when the current one is reloaded, moving forward only. The disadvantage of this is that we can't get to specific images that we liked and want to return to, which can be problematic the larger our gallery is. Duplicates also shouldn't appear on the screen (this is common-sense, but still worth repeating).
An idea we might have is to generate one big sprite image and get all photos in a single HTTP request. Although this works well with the PNG format (which is less suitable for photos), it can show some artifacts when many JPEG photos are compressed together. If we pay attention about quality, we should probably keep the JPEGs separate. We can't show too many of them, and the browser is already good at pipelining and caching. We can also have appropriate “alt” attributes to describe what is currently seen on the image, which will improve accessibility compared to the sprited version.
There are special challenges when showing images on a small screen. They can become too small, almost invisible. In this case it seems a good idea to offer a preview that has the largest possible size. When the user hovers over a small image, the preview updates with an enlarged version of it. We can take the dimensions of this enlarged version and create a gallery with many such images. This way we ensure that on the smallest screen we can reuse the work that is already visible on a bigger screen. Otherwise we would need to generate images of sizes specific for the small screen, which introduces overhead.
Touch events are another thing that may not work as we intend (I have no way to test this). Moving the finger from point A to point B should not make our page behave in strange ways. Last, but not least, there are various devices with different browsers and each of them may behave differently.
Here is my humble attempt to create a simple photo gallery in the knowledge that many such projects have been done before. It is nothing original and you may experience some problems. Many photos are just so beautiful that they deserve your own gallery. The images in this one were taken from unsplash.com. I hope that you will like them.