Iâm gonna take more photos this year so I got a camera! here are some photos I took with that camera to start learning how to take photos.
itâs nice, taking photos. forces me to get out of my own dang head and đď¸observeđď¸ whatâs going on around me. I also want to take more & better show photos!
the images lazy load and I put a big scroll zone in case you wanted to bail before loading any of the images, no worries.
if youâre still here, scroll down a bunch if you want to see several photos of a cat and a park.
á
pico (a cat)
fort greene park (a park)
á
if youâve made it this far, đcongratulationsđ! you are rewarded with more words.
as you might tell, I stress about using images because I want to be sure theyâre worth your bandwidth. to absolve some of my guilt, I try to squish files small as possible using Squoosh, preferring AVIF since it squooshes the smallest with the least perceptual damage.
annoying thing about AVIF, though: it doesnât do any progressive loading, straight blankness until itâs good and ready. Iâm using smallest images I can but for slower connections having nothing to indicate an image is loading is not a good experience.
also annoying, generally speaking, not specific to AVIF: content layout shift. gonna wanna statically set the width and height, which is tedious to do manually since not all the images have the same geometry!
I wrote a script that solves both problems and generates the image tags for me:
<img
loading="lazy"
src="/static/20250103/2024-12-31T233223_01.31ef7a37602fee9777d46e21cae48e5f.avif"
width="2048"
height="1365"
style="background: url('data:image/avif;base64,AAAAHGZ0eXBhdmlmAAAAAGF2aWZtaWYxbWlhZgAAANZtZXRhAAAAAAAAACFoZGxyAAAAAAAAAABwaWN0AAAAAAAAAAAAAAAAAAAAAA5waXRtAAAAAAABAAAAImlsb2MAAAAAREAAAQABAAAAAAD6AAEAAAAAAAABCwAAACNpaW5mAAAAAAABAAAAFWluZmUCAAAAAAEAAGF2MDEAAAAAVmlwcnAAAAA4aXBjbwAAAAxhdjFDgQAMAAAAABRpc3BlAAAAAAAAAGYAAABEAAAAEHBpeGkAAAAAAwgICAAAABZpcG1hAAAAAAAAAAEAAQOBAgMAAAETbWRhdBIACgkYGbLDYICGg0Iy+wESAAKKKKEAtmmELaEyfsyfa/FK949D3bdOU+wyGwLap9ONFch1ws+gxlO14WshlmebwmGmAO9u3bapZLRCgG0ee/WnrfAvYKnjOwx0vpEjI7ZT0jOKAe88s/7/GaF9gJ1I0aNbC2dhq10HznJhkCdoYrZ3Vz2U4lt8SCPT9deGtzR1RourCJIB0KrxEyKjAyBAJ31bEtJZBxnipI9L1gCvH442LsfyXbWa2qxZwfNMT59zg3aoy6EdikSbddjYoVmaMC3XtS7Xl0G1AQrwXy1mBO4pLbGYJUNhl+tv1yfdp4NywrNJXtHIXd+M1yb4zSCOBi5T0lezHLGqew==');"
/>
I use imagemagick to get the width and height and that data URL is a blurry 5% sized version of the image to stick as the background while the real deal loads:
magick "$file" -resize 5% -blur 0x3 - | base64
I have a different script that renames the files to include a content hash so I can cache them forever.
itâs all a little jankily thrown together right now but the workflow is not bad:
- throw images in a new folder under
static/
- run
scripts/hashname.sh static/$folder/*
- run
scripts/gengallery.sh static/$folder/* | pbcopy
- paste into the page
appendix a: gengallery.sh
#!/bin/bash
main() {
# check if at least one argument (file) is provided
if [ "$#" -lt 1 ]; then
echo "Usage: $0 <file1> [file2 ...]"
exit 1
fi
# base URL for the `src` attribute in the HTML
BASE_URL="/"
# loop through all provided files
for file in "$@"; do
# ensure the file exists
if [ ! -f "$file" ]; then
echo "File $file does not exist, skipping." >&2
continue
fi
# get the dimensions of the image
dimensions=$(magick identify -format "%wx%h" "$file" 2>/dev/null)
if [ $? -ne 0 ]; then
echo "Failed to get dimensions for $file, skipping." >&2
continue
fi
# extract width and height
width=$(echo "$dimensions" | cut -dx -f1)
height=$(echo "$dimensions" | cut -dx -f2)
# remove leading "public/" from the file path
relative_path=${file#public/}
# generate the base64-encoded data URL for the background
data=$(magick "$file" -resize 5% -blur 0x3 - | base64)
if [ -z "$data" ]; then
echo "Failed to generate data URL for $file, skipping." >&2
continue
fi
# output the HTML <img> tag with the data URL in the background style
echo "<img loading=lazy src=\"$BASE_URL$relative_path\" width=\"$width\" height=\"$height\" style=\"background: url('data:image/avif;base64,$data');\">"
done
}
main "$@"
appendix b: hashname.sh
#!/bin/bash
set -eu
usage() {
echo "Usage: $0 [--undo] <file1> [file2] ... [fileN]"
exit 1
}
# parse arguments
undo_mode=false
if [ "$#" -lt 1 ]; then
usage
fi
if [ "$1" == "--undo" ]; then
undo_mode=true
shift
fi
main() {
for file in "$@"; do
# skip if the file does not exist or is not a regular file
if [ ! -f "$file" ]; then
echo "Skipping $file (not a valid file)"
continue
fi
if $undo_mode; then
# undo mode: remove hash from filename
filename=$(basename -- "$file")
extension="${filename##*.}"
name="${filename%.*}"
# check for hash-like pattern (32 hexadecimal characters) in the name
new_name=$(echo "$name" | sed -E 's/\.[a-f0-9]{32}$//')
if [ "$new_name" != "$name" ]; then
# rename to remove the hash
new_filename="${new_name}.${extension}"
mv "$file" "$(dirname "$file")/$new_filename"
echo "Renamed $filename to $new_filename"
else
echo "Skipping $file (no hash found)"
fi
else
# normal mode: Add hash to the filename
# extract file name and extension
filename=$(basename -- "$file")
extension="${filename##*.}"
name="${filename%.*}"
# check if the file already has a hash suffix
if [[ "$name" =~ \.[a-f0-9]{32}$ ]]; then
echo "Skipping $file (already has hash suffix)"
continue
fi
# calculate MD5 hash of the file content
md5_hash=$(md5 -q "$file")
# create the new filename with MD5 hash
new_filename="${name}.${md5_hash}.${extension}"
mv "$file" "$(dirname "$file")/$new_filename"
echo "Renamed $filename to $new_filename"
fi
done
}
main "$@"