How I crafted TL;DRs with LLMs and modernized my blog (part 4)

tl;dr: I optimized all my blog images for performance, making Lighthouse happy. I fixed slow zooming by tweaking the Cache-Control HTTP header on Netlify. Improving alt text with GPT-4.1 made me wonder how I ever managed without an LLM in my toolkit!
View the series
  1. See how I crafted story-like tl;drs for my posts with LLMs
  2. Learn how I generated my llms.txt summary with LLMs
  3. Grab the "Copy page" button's code, as seen in OpenAI docs
  4. Check how I optimized images for better blog performance
  5. See how impressed I was by GPT-4.1's meta descriptions

To improve performance, I used Lighthouse ↗. Lighthouse mostly complained about images, so most of my work went into optimizing them.

This was expected. I hadn't done much about images before, other than converting original png files to webp.

Using responsive images

I started by adding responsive images and lazy loading those that are off-screen:

<img
  src="/img/original/2025-08-12-sheets2gmail-spreadsheet.webp"
  srcset="
          /img/420/2025-08-12-sheets2gmail-spreadsheet.webp   420w,
          /img/640/2025-08-12-sheets2gmail-spreadsheet.webp   640w,
          /img/840/2025-08-12-sheets2gmail-spreadsheet.webp   840w,
          /img/1280/2025-08-12-sheets2gmail-spreadsheet.webp 1280w
          "
  sizes="(max-width: 640px) 100vw, 640px"
  width="1202"
  height="988"
  loading="lazy"
  alt="Automating Gmail sends with Google Sheets updates"
/>

Why these specific widths?

Because, except for my profile picture, images are always inside an element capped at 640px max-width. And considering today's mobile screens max out at about 410px wide and device pixel ratios (DPR) can be 2 or 3, these sizes make sense:

  • 420,

  • 640,

  • 840 (420 × 2DPR) and

  • 1280 (420 × 3DPR).

To resize the images, I used the convert utility from ImageMagick ↗ with this script:

#!/usr/bin/env bash

for size in 420 640 840 1280; do
  echo "Resizing => $size"
  mkdir -p ./assets/img/$size
  fd -e webp . ./assets/img/original | while read -r src; do
    filename=$(basename "$src")
    target="./assets/img/$size/$filename"
    if [ ! -f "$target" ]; then
      convert "$src" -resize ${size}x "$target"
      echo "  $filename"
    fi
  done
done

The original images are in the ./assets/img/original/ directory. They're resized into the 420, 640, 840, and 1280 subdirectories under ./assets/img/.

For images that are in the viewport when the page first loads (like in this post), I explicitly set the loading attribute to eager.

Get my latest AI automation experiments — free

No spam. Unsubscribe anytime.

Prefetching full-size images for zoom

All images can be zoomed by clicking on them. When you do, the original image (set in src on <img>) is used.

Zoomed-in view of the Detailed OpenAI Traces user interface, highlighting AI agent workflow steps and span analytics for real-world automation processes, as shown in a modal window.  Screenshot from tonyaldon.com, exploring practical LLM automation, troubleshooting, and workflow optimization.

Now that I use responsive images, the full-size version isn't always loaded by default. This caused some lag when clicking to zoom.

To fix that, I added this snippet to prefetch full-size images after DOMContentLoaded, so zooming in is instant:

document.addEventListener("DOMContentLoaded", function () {
  document.querySelectorAll("img").forEach(function (img) {
    const preImg = new Image();
    preImg.src = img.src;
  });
});

Funny enough, prefetching wasn't enough, there was still some lag. Checking the Network tab in DevTools showed the src image was fetched twice: once for prefetch, and again when zooming.

Why?

Because the Cache-Control HTTP header from my dev server (browsersync ↗) was set to public, max-age=0. This tells the browser to check page with the server on every use, leading to a 304 Not Modified and a delay.

Screenshot of Chrome DevTools Network tab displaying a refetched image from tonyaldon.com, highlighting the HTTP Cache-Control header and 304 Not Modified status code. Demonstrates efficient image caching and HTTP header configuration for AI automation blog.

Changing Cache-Control for these images to public, max-age=31536000 fixed the refetching problem.

Since I use Netlify ↗, I just added this _headers file at my project's root:

/img/original/*
  Cache-Control: public, max-age=31536000
/img/420/*
  Cache-Control: public, max-age=31536000
/img/640/*
  Cache-Control: public, max-age=31536000
/img/840/*
  Cache-Control: public, max-age=31536000
/img/1280/*
  Cache-Control: public, max-age=31536000

I haven't found a way to configure browser-sync the same way locally.

Preloading the profile picture

My profile picture appears in the viewport as soon as the page loads (on desktop). I considered inlining it as base64 data right in the HTML, but ended up adding a <link> tag in the head to tell the browser to fetch it early, at the right size. This makes me happy enough, even if I still wish it appeared instantly with the text.

<link
  rel="preload"
  as="image"
  imagesrcset="/img/tony-aldon-profile-picture-90x90.webp 90w,
               /img/tony-aldon-profile-picture-180x180.webp 180w,
               /img/tony-aldon-profile-picture-270x270.webp 270w"
  imagesizes="90px"
/>

Improving image descriptions with GPT-4.1

Finally, I improved my hand-written image descriptions (alt attributes) by giving them to GPT-4.1 using this prompt:

I'm writing alt text for images in my blog about AI automation, which
can be described as follows:

This blog is my ongoing, hands-on exploration of AI automation—how to
build, troubleshoot, and refine real-world workflows using LLMs,
automation platforms like Zapier, and both code-based and no-code
tools. Everything here comes from my lived experience learning as I
go, with lots of focus on what's practical, what breaks, and what you
can actually reuse.

I'm Tony Aldon and my blog is served at tonyaldon.com.

I want to optimize the alt attributes for SEO.  Can you improve the
following 29 descriptions, and provide a summary explaining why your
improvements are beneficial for SEO?

Here are the descriptions I finally kept. Most are identical to what GPT-4.1 suggested. I've listed each original and improved version next to each other for comparison:

[original] Google sheets example automation - Send Gmail when Google Sheets updates
[improved] Automating Gmail sends with Google Sheets updates

[original] Google Cloud console - Creating new project sheets2gmail
[improved] Google Cloud Console: creating a new project for Google Sheets to Gmail automation

[original] Google Cloud console - Enabling Google Sheets API
[improved] Enabling Google Sheets API in Google Cloud for automation project

[original] Google Cloud console - Enabling Gmail API
[improved] Activating Gmail API in Google Cloud to enable automated email sending

[original] Google Cloud console - Google Auth platform - Setting up project sheets2gmail
[improved] Setting up authentication for AI automation project sheets2gmail on Google Cloud

[original] Adding test users to Google Auth for sheets2gmail automation project
[improved] Google Cloud console - Google Auth platform - Put aldon.tony@gmail.com test users in project sheets2gmail

[original] Google Cloud console - Google Auth platform - Create OAuth client ID in project sheets2gmail
[improved] Creating Google OAuth client ID for secure workflow automation in sheets2gmail

[original] Google Cloud console - credentials information in project sheets2gmail
[improved] Viewing API credentials for Google Sheets to Gmail automation project

[original] Sign in with Google
[improved] Sign-in prompt using Google account for automation project

[original] Sign in with Google - sheets2gmail wants access to your google account
[improved] Google sign-in permissions request from sheets2gmail automation app

[original] Google sheets example for Zapier automation - Send Gmail when Google Sheets updates
[improved] Google Sheets and Zapier integration: automate Gmail email sending on sheet updates

[original] Zapier Zap editor - Automation - Send Gmail when Google Sheets updates
[improved] Zapier Zap editor: building Gmail automation triggered by Google Sheets changes

[original] Zapier Zap history dasboard
[improved] Zapier Zap history dashboard showing execution logs for automated workflows

[original] Troubleshooting a Zapier Zap run in the Zap editor - Automation - Send Gmail when Google Sheets updates
[improved] Troubleshooting a Google Sheets to Gmail automation run in Zapier Zap editor

[original] Troubleshoot tab with AI generated information in Zapier Zap editor
[improved] AI-generated troubleshooting suggestions in Zapier automation editor

[original] Troubleshoot tab with AI generated information in Zapier Zap editor
[improved] AI-powered diagnostic tab showing error info in Zapier automation editor

[original] Zapier Zap editor showing no information in Logs tab given the error is due to missing required information
[improved] Zapier editor Logs tab empty due to missing required automation input data

[original] Zapier notice indicating they stopped integrating with Twitter API
[improved] Zapier platform notice: end of Twitter API integration for automations


[original] OpenAI best practices to define function tools in the context of AI agents
[improved] OpenAI documentation: best practices for defining function tools in AI agent workflows

[original] AI agents, Responses API, function tools and handoffs visualized in OpenAI Traces UI
[improved] Visualization of AI agents, API responses, and function tool handoffs in OpenAI Traces UI

[original] AI agents and OpenAI Traces dasboard
[improved] OpenAI Traces dashboard monitoring AI agent workflows in automation

[original] AI agents and Responses API visualized in OpenAI Traces UI - Agent workflow on the left and span details on the right
[improved] OpenAI Traces UI: displaying AI agent workflow and span details for Responses API

[original] AI agents, Responses API, function tools visualized in OpenAI Traces UI
[improved] Analysis of AI agents and function tool calls in OpenAI Traces for workflow automation

[original] AI agents, Responses API, function tools visualized in OpenAI Traces UI
[improved] OpenAI Traces: visual report of Responses API, function tool use, and agent actions

[original] AI agents, Responses API, function tools and handoffs visualized in OpenAI Traces UI
[improved] Visualization of AI agents, handoffs, and tool calls in workflow automation with OpenAI Traces

[original] AI agents, Responses API, function tools and handoffs visualized in OpenAI Traces UI - Agent workflow on the left and span details on the right
[improved] Detailed OpenAI Traces UI showing AI agent workflow and span analytics for automation

[original] Monitor Stripe disputes in dashboard
[improved] Monitoring and managing Stripe payment disputes in Stripe dashboard for automation

[original] Post by Wade Foster, Co-founder/CEO of Zapier - If you're an AI automation engineer, we'll hire you
[improved] Zapier co-founder Wade Foster's post about hiring AI automation engineers

[original] Zapier job - AI automation expert
[improved] Zapier careers: open job listing for AI automation expert engineering role

Maybe next time, I'll try giving the images directly to GPT-4.1 and see how it handles them.

That's all I have for today! Talk soon 👋

Recent posts
latestHow I crafted TL;DRs with LLMs and modernized my blog (part 5)
See how impressed I was by GPT-4.1's meta descriptions
prompt
How I crafted TL;DRs with LLMs and modernized my blog (part 3)
Grab the "Copy page" button's code, as seen in OpenAI docs
code
How I explored Google Sheets to Gmail automation through Zapier before building it in Python (part 1)
See how I built my first Zapier Gmail alert from Sheets updates
no-code
How I realized AI automation is all about what you automate
Check my favorite CRM AI automation (so far) from Zapier blog
misc
Curious about the tools I use?