The Images Page

the images page on my site lists every single image I've ever published on my website. I'm actually quite happy with how it all works, and I realised I've never broken it down before!

Before uploading any images to my site, I need to compress them. Images out of my camera are huge and I don't want people downloading 25mb+ images on random pages of my site. I use ImageMagick and a little helper function I wrote to do this.

I generate a sm, md and lg version of each image with the following commands:

# A thumbnail version of the image
convert originalImage.jpg -strip -interlace Plane -scale 10% -blur 0x2.5 -resize 1000% -resize 180 "sm/${1%.*}.jpg"

# A suprisingly good quality version of the image
convert originalImage.jpg -strip -interlace Plane -gaussian-blur 0.05 -quality 85% -resize 720 "md/${1%.*}.jpg"

# A nicer "HD" version of the image
convert originalImage.jpg -strip -quality 85% -interlace Plane -resize 1800 "lg/${1%.*}.jpg"

Once I've generated all the images, they're added to my sites storage in folders. My site generator then reads all the files in the sm folder, and generates a html for all the images. The html for each image looks something like this:

<img 
  src="https://assets.pfy.ch/sm/image.jpg" 
  finalSrc="https://assets.pfy.ch/md/image.jpg" 
  hdSrc="https://assets.pfy.ch/lg/image.jpg" 
/>

By default the sm version of the image will load, when the images is in view, I swap out the src with the value in the finalSrc attribute. This is done using an Intersection Observer.

const images = document.querySelectorAll('.galleryImage');

const loadImage = (image: HTMLImageElement) => {
  image.src = image.getAttribute('finalSrc') || '';
};

const handleIntersection = (entries: IntersectionObserverEntry[]) => {
  entries.forEach((entry) => {
    if (entry.intersectionRatio > 0) {
      loadImage(entry.target as HTMLImageElement);
    }
  });
};

const observer = new IntersectionObserver(handleIntersection, {
  root: null,
  rootMargin: '0px',
  threshold: 0.05,
});

images.forEach((img) => {
  observer.observe(img);

  img.addEventListener('click', (event) => {
    const image = event.target as HTMLImageElement;

    image.onerror = () => {
      console.warn('Could not get LG version of image!');
      image.src = image.getAttribute('finalSrc') || '';
    };

    image.src = image.getAttribute('hdSrc') || '';
  });
});

I would love to have the images popup or go fullscreen when they're clicked, but I didn't want to make it too complex.

Since they're just plain HTML images, you can just open them in a new tab or window. Which seems to work fine for most people who browse my photos!



This page was originally published 27 Mar 2025

Updated 205 days ago (27 Mar 2025)