In the end I ended up using Next.js as a static site generator that pulls all the routes from my directory structure, making it possible to add new photography collections and filters as I go.
Might be overkill for the use case but it was fun to learn. The irony is I had to write a bunch of JS to produce it.
Still need to optimize the image sizes and I am thinking about adding filters for b&w/color/format.
I use a utility called jhead to resize, fix rotation issues, and rename photos by date - then I tied this to a folder action on macos so I can just drop photos in a folder and they get renamed and resized.
Then Hugo has this cool 'smart' cropping feature which tries to crop based on content  - and the end result is now all I do is drop photos in a folder and publish and it comes out looking pretty good .
I ended up using sharp  since it was so easy to integrate into my workflow.
No JS would have been nice, but ended up making the content draw and re-flow in JS as I wanted to keep the aspect ratio of the thumbnails instead of showing a bunch of squares, for which a simple flexbox would have been enough.
I still have to write a decent README
OP, have you tried loading="lazy" ? I don't know if it works with the picture tag but it is worth trying I think.
Very often, webpages contain many images that contribute to data-usage and how fast a page can load. Most of those images are off-screen (non-critical), requiring user interaction (an example being scroll) in order to view them.
The loading attribute on an <img> element (or the loading attribute on an <iframe>) can be used to instruct the browser to defer loading of images/iframes that are off-screen until the user scrolls near them.
For those on mobile and can't right click
<source srcset="https://static.hansenzhang.com/travel/places/2019-07-04-nort... type="image/webp">
<source srcset="https://static.hansenzhang.com/travel/places/2019-07-04-nort... type="image/jpeg">
<img src="https://static.hansenzhang.com/travel/places/2019-07-04-nort... alt="travel/places/2019-07-04-northcarolina-3-color.jpeg">
I was curious about the picture tag. Here is what Mozilla documentation says https://developer.mozilla.org/en-US/docs/Web/HTML/Element/pi... :
The HTML <picture> element contains zero or more <source> elements and one <img> element to offer alternative versions of an image for different display/device scenarios.
The browser will consider each child <source> element and choose the best match among them. If no matches are found—or the browser doesn't support the <picture> element—the URL of the <img> element's src attribute is selected. The selected image is then presented in the space occupied by the <img> element.
To decide which URL to load, the user agent examines each <source>'s srcset, media, and type attributes to select a compatible image that best matches the current layout and capabilities of the display device.
The <img> element serves two purposes:
It describes the size and other attributes of the image and its presentation.
It provides a fallback in case none of the offered <source> elements are able to provide a usable image.
Common use cases for <picture>:
Art direction. Cropping or modifying images for different media conditions (for example, loading a simpler version of an image which has too many details, on smaller displays).
Offering alternative image formats, for cases where certain formats are not supported.
Saving bandwidth and speeding page load times by loading the most appropriate image for the viewer's display.
If providing higher-density versions of an image for high-DPI (Retina) display, use srcset on the <img> element instead. This lets browsers opt for lower-density versions in data-saving modes, and you don't have to write explicit media conditions.
The resolution scaling is a good idea as well. I used the picture tag initially as a fallback for browsers that don't support webp images. More importantly I need to actually create scaled images which I have been putting off...
Thanks for your comments/advice!
Neat photos though.