Performance Tracing with Chrome DevTools MCP and The AI Agent

In a previous post, Improving Lighthouse Performance with Image Optimization, I described a manual process of auditing image delivery, converting to WebP, and tuning LCP/CLS to push my Lighthouse score from 56 to 85. That workflow was effective but slow — run a trace, read the output, decide what to fix, repeat.
This time I went further. Instead of running Lighthouse by hand, I used the Chrome DevTools MCP, a tool that gives AI Agents direct access to a Chrome browser session. The result was a faster, more iterative loop: The AI Agent ran the traces, read the insights, proposed fixes, and measured the results, all within the same conversation.
How the Chrome DevTools MCP works
The Chrome DevTools MCP exposes browser automation primitives (navigate, evaluate script, take screenshots, start/stop performance traces) as tools that The AI Agent can call directly. With Chrome launched in remote debugging mode, The AI Agent can open a URL, run a performance trace, parse the LCPDiscovery, CLSCulprits, and ImageDelivery insights, and reason about them without you switching tabs.
The workflow looked like this:
- Launch Chrome with
--remote-debugging-port=9222 - Ask The AI Agent to trace a page
- The AI Agent navigates, starts the trace, interacts with the page, stops the trace, and reads the insights
- The AI Agent proposes a fix, I apply it and deploy
- The AI Agent re-runs the same trace and compares metrics
What made this faster than doing it manually: no copy-pasting metrics, no explaining what the Lighthouse panel said, no context switching. The entire trace-analyse-fix cycle lived in the conversation.
The page under test: /moments
My moments page is a photo gallery with 92+ photos loaded in batches of 12 via a "Load More" button. Each photo opens a modal with carousel navigation and metadata. It is one of the heavier pages on the site (a good stress test).
I ran three separate trace types: page load (LCP), repeated "Load More" interactions (INP + image delivery), and full modal navigation across all 92 photos (INP + CLS).
Finding 1: LCP image was deprioritised
The first trace flagged LCPDiscovery — the first grid image had loading="lazy" applied unconditionally, so the browser only discovered and fetched it after JS hydration.
Fix: loading="eager" and fetchPriority="high" on the first image only.
| Metric | Before | After |
|---|---|---|
| LCP | 327 ms | 219 ms |
| Load delay | 48 ms | 5 ms |
| Render delay | 238 ms | 177 ms |
Finding 2: Grid thumbnails served at full resolution
The ImageDelivery insight flagged 7.4 MB of avoidable downloads on a single "Load More" click. All 92 grid thumbnails were being served at original resolution (up to 3072×3072) despite being displayed at ~400px per cell.
Fix: A new script (scripts/images/generate-moment-thumbs.js) reads all the photos in the gallery and generates a <name>-thumb.webp at 800px max for every grid photo. Full-res originals are preserved and still used in the modal.
| Metric | Before | After |
|---|---|---|
| Download per "Load More" (12 images) | ~7.4 MB | ~650 KB |
| Total grid image storage (92 images) | 32.19 MB | 7.24 MB |
| LCP (compounded with fix 1) | 327 ms | 120 ms |
The LCP improvement compounded: the thumbnail is now small enough to load and render before hydration finishes, collapsing render delay from 238 ms to 73 ms.
Finding 3: CLS 0.46 during modal navigation
After deploying thumbnails, a third trace navigated through all 92 photos programmatically (80 ms per step). CLS spiked to 0.46 — well above the "Bad" threshold of 0.25.
The root cause: .panel in MomentModal.tsx used max-height: 90vh. Because max-height allows the panel to grow and shrink with image content, every photo swap with a different aspect ratio reflowed the panel and accumulated layout shift. Over 92 navigations that adds up fast.
Fix: One CSS change — max-height: 90vh → height: 90vh. With a fixed height the panel never reflows. object-fit: contain on .photo handles all aspect ratios without distortion.
| Metric | Before | After |
|---|---|---|
| CLS (92-photo modal navigation) | 0.46 ⚠️ | 0.01 ✅ |
The remaining 0.01 is unrelated to modal image swaps.
What I would have missed without scripted traces
The CLS issue in particular would have been very hard to catch manually. It only accumulated to 0.46 over 91 rapid navigations. A normal user browsing a few photos at a time would see a much lower CLS score — low enough to fly under the radar in a standard Lighthouse audit. Running the trace programmatically across all 92 photos made the pattern visible.
The faster loop also meant I could verify each fix before moving to the next one, rather than batching everything and guessing which change caused which improvement.
Summary of all metrics
| Metric | Baseline | After all fixes | Delta |
|---|---|---|---|
| LCP | 327 ms | 120 ms | -63% |
| INP (modal navigation) | 167 ms | 40 ms | -76% |
| CLS (modal navigation) | 0.46 ⚠️ | 0.01 ✅ | -98% |
| Grid image download (per batch) | ~7.4 MB | ~650 KB | -91% |
Glossary
LCP — Largest Contentful Paint Measures how long it takes for the largest visible element (image, heading, or block of text) to finish rendering in the viewport. A good LCP is under 2.5 s. It is the primary signal for perceived load speed.
INP — Interaction to Next Paint Measures the latency between a user interaction (click, tap, key press) and the next frame the browser paints in response. A good INP is under 200 ms. It replaced FID as a Core Web Vital in 2024.
CLS — Cumulative Layout Shift Measures the total amount of unexpected visual movement that occurs during the page's lifetime. A good CLS is under 0.1. High CLS means elements jump around as the page loads, which disrupts reading and can cause accidental clicks.
FCP — First Contentful Paint Measures when the browser renders the first piece of DOM content (text, image, SVG). Marks the point where the page stops looking blank to the user.
FID — First Input Delay (deprecated — replaced by INP) Measured the delay between a user's first interaction and the browser's response. Retired as a Core Web Vital in March 2024.
TTFB — Time to First Byte Measures the time from the browser sending a request to receiving the first byte of the server response. Affected by server processing time, CDN configuration, and network latency.
Changelog (2)
- Added Glossary section with definitions and MDN links for LCP, INP, CLS, FCP, FID, and TTFB.
- Initial publish.