Direct Answer
The models did not solve T3 Code's idle-performance bug because they began with plausible code stories instead of isolating the system that was actually busy. GPT-5.6 Sol first blamed React, WebSocket traffic, history scans, persistence, and syntax highlighting. That produced a very large CPU-focused change without materially reducing Theo's GPU-process load. Fable later found useful animation and visual-effect candidates, but Theo still had to identify the causal combination and correct the visual regressions.
The final patch is much more interesting than the headline. Infinite pulse and ping indicators kept the browser compositor producing frames at every vsync. On Theo's 120 Hz display, that meant an otherwise idle page never became visually idle. A fixed, full-viewport SVG grain overlay then forced each of those frames through an expensive blend over the whole window. In the merged patch's recorded test, changing both sides of that interaction reduced idle GPU utilization from more than 30% to about 4% with indicators active.
Watch the Postmortem
Video and incident credit: Theo - t3.gg. Watch the original video, follow @theo on X, and inspect the open-source T3 Code repository. The video contains a Blacksmith sponsorship. This article is independent and not sponsored.
The phrase "Fable broke my app" is Theo's framing. The repository record is more nuanced: an earlier version of the UI included model-assisted visual choices, Fable co-authored parts of the CPU investigation, and the final GPU patch also carries a Fable co-author credit. The useful conclusion is not that one model is uniquely bad. It is that fluent code generation and causal diagnosis are different capabilities.
The Incident in One Table
| Stage | Observation | What it suggested | What happened next |
|---|---|---|---|
| Initial symptom | T3 Code felt responsive, but the browser GPU process consumed roughly 13-20% at lower resolution and reportedly approached 50% on Theo's 5K 120 Hz setup. | The bottleneck was not ordinary input latency or a slow network request. | Theo reproduced the load in a clean Chrome window with only T3 Code open. |
| First agent diagnosis | Sol focused on WebSockets, history scans, React updates, persistence, and highlighting. | A familiar CPU and main-thread performance story. | A change exceeding 10,000 lines did not materially improve the GPU symptom. |
| Process isolation | Opening Google in the isolated browser reduced the GPU process sharply. | The browser and machine were capable of idling; T3 Code triggered the work. | Theo shifted from application logic to visual rendering and compositing. |
| Broad disable test | A generated console helper disabled animations, transitions, filters, shadows, blur, media, composer effects, and grain. | At least one item in the visual stack was causal. | GPU load fell to roughly 3%; resetting the page restored the load. |
| Manual bisection | Disabling animations produced the largest immediate reduction. | Persistent animation, not React traffic, was the dominant path. | The investigation narrowed to small pulse and ping status indicators. |
| Interaction found | Small infinite animations were expensive mainly when combined with a fixed full-screen grain layer and other composited effects. | The bug was an interaction, not one obviously expensive component. | The final patch changed animation timing and moved grain behind content. |
| Patch measurement | The PR reports more than 30% idle GPU before and about 4% after on a 120 Hz display. | The hypothesis survived a before-and-after test. | Visual seams introduced during the optimization required two follow-up fixes. |
Those percentages are creator and pull-request measurements, not portable benchmarks. Browser, operating system, resolution, refresh rate, open windows, HDR behavior, DevTools state, and active status indicators all change the result. Theo's own final scare illustrates the problem: after shipping the fix, GPU usage still looked high because two separate Claude pages were open. Closing those pages produced the expected T3 Code result.
Why the Agents Guessed Wrong
Sol's first answer sounded like a senior performance review because the suspected paths were real paths: event streams can fan out, reducers can repeatedly sort history, persistence can serialize too often, and syntax highlighting can become expensive. The related CPU performance pull request documents legitimate work on bounded replay, incremental projections, cached timelines, debounced persistence, and sidebar prewarming.
The problem was not that every observation was false. The problem was that the proposed mechanism did not predict the measured symptom. An idle-looking page with a busy browser GPU process, tiny scripting and layout activity, and a large increase on a high-refresh high-DPI display should reduce confidence in a server-and-React diagnosis. The agent optimized code it could explain instead of first proving that code controlled the metric Theo cared about.
| Failure mode | How it appeared here | Better instruction |
|---|---|---|
| Prior-driven diagnosis | The model selected common React and streaming hot paths. | List hypotheses by subsystem, then name the observation that would falsify each one. |
| Summary inheritance | A Chrome-generated AI summary became evidence even though it did not fit the process-level behavior. | Treat profiler summaries as leads; inspect raw traces and run controlled toggles. |
| Large speculative patch | Thousands of lines changed before a causal variable was isolated. | Require a minimal experiment that changes the target metric before product code is edited. |
| Single-component thinking | The agents searched for one expensive animation or component. | Test interactions among animation, layer promotion, filters, overlays, resolution, and refresh rate. |
| Tool mismatch | Main-thread tools showed little activity while compositor work continued elsewhere. | Combine process monitoring, browser traces, rendering tools, isolated tabs, and repeatable page states. |
| Premature success language | The agent declared a performance fix before Theo's target metric improved. | Define acceptance as a measured threshold on representative hardware, not a clean diff or passing tests. |
The Useful Pivot: Make the Agent Build the Experiment Harness
At about 11:42, Theo changes the job. The agent is no longer asked to know the answer. It is asked to create a production-page console helper, exposed as window._t3gpu, that can turn suspect visual systems on and off without rebuilding the application after every theory.
The generated harness grouped candidates such as:
- CSS animations and transitions;
- filters, blur, shadows, and backdrop effects;
- the chat composer and full-screen grain layer;
- media and other elements likely to receive their own compositing layer;
- individual pulse, ping, status, and typing indicators.
Applying every override damaged the interface, but that was acceptable for diagnosis. The GPU process dropped to roughly 3%. Resetting the helper restored the high load. That one reversible A/B result contributed more than the earlier large patch because it established that the causal path lived somewhere in the visual stack.
Theo then separated animation from transition rules and tested categories individually. Noise alone helped a little. Blur alone did not explain the load. Disabling animations produced the large drop. A second generated tool enumerated and paused active animations, letting him test small status elements instead of reading the entire CSS and component tree manually.
The Real Root Cause Was an Interaction
The visible offender looked absurdly small: a pulsing terminal icon in the sidebar, plus related typing and thread-status indicators. Tailwind's ordinary animate-pulse and animate-ping utilities are not inherently broken. In this page, however, several infinite animations kept changing opacity or scale indefinitely. On a 120 Hz display, the compositor had a reason to produce another frame up to 120 times each second even when the application data was quiet.
That still does not explain the full cost. The page also had a low-opacity SVG feTurbulence grain texture attached as a fixed body::after overlay across the viewport. Each animation frame had to be composited with that full-window layer. High resolution increased the pixel area; high refresh rate increased how often the work repeated. Backdrop blur and other surfaces made the layer tree more sensitive, but the final patch identifies the always-changing indicators plus fixed grain as the decisive combination.
small infinite status animations
x
120 Hz refresh rate
x
high-DPI / 5K pixel area
x
fixed full-viewport grain overlay
=
expensive frames while the app appears idle
This is why model confidence was misleading. None of the ingredients looks catastrophic in isolation. The expensive behavior emerges from the browser's rendering architecture and the exact display environment. Source-code search can find the pieces; only measurement can establish their interaction.
What the Final Patch Actually Changed
The three-commit GPU performance patch did not simply remove animation. It preserved status motion while reducing how often the animated value changed, and it removed the grain texture from the per-frame blend path.
| Before | After | Reason |
|---|---|---|
Infinite Tailwind animate-pulse at terminal, project, thread, and typing indicators. | Custom animate-status-pulse with long flat opacity holds and short stepped ramps. | The value changes during roughly 20% of the cycle instead of continuously. |
Infinite animate-ping at connection, browser cursor, recording, and local-server indicators. | A stepped burst followed by a long invisible hold. | The UI keeps immediate feedback without continuous interpolation. |
Fixed body::after grain overlay above the entire app. | Grain baked into the body background and selected chrome surfaces. | The texture can be cached with each surface instead of re-blended over every animated frame. |
| Removing the overlay darkened some surfaces and exposed seams. | A reusable surface-grain utility restores grain to the sidebar and inset surfaces. | Performance fixes still need visual parity, especially across SDR and HDR displays. |
| The lower composer strip mixed transparency from a lighter card color. | The strip mixes from the page background while retaining backdrop blur. | It eliminates the lighter full-width seam without discarding the frosted effect. |
The pull request reports a reduction from more than 30% to about 4% idle GPU utilization on a 120 Hz display with indicators active. The video reports higher peaks on a 5K setup and lower numbers at 720p. Both can be true because they describe different measurement conditions. The honest unit is not "T3 Code uses X percent." It is a reproducible before-and-after result on a declared device, browser, viewport, refresh rate, and page state.
Compositor-Only Is Fast, Not Free
Standard web-performance guidance recommends animating transform and opacity because the browser can often handle them during composition without repeating layout and paint. That guidance remains correct. The mistake is upgrading "usually cheaper" into "costless in every layer tree."
MDN's animation guidance notes that code-driven animations can still consume CPU and that layer-promoted transforms and opacity changes move work to composition. Chrome's runtime-performance documentation similarly treats animation and idle behavior as measurable phases, and recommends recording traces, checking FPS and CPU, and reducing the work visible in each frame. T3 Code adds an important field example: a composited animation can be smooth and still waste energy because it prevents the page from settling.
- High refresh rate multiplies opportunity: a 120 Hz screen can request twice as many frames as a 60 Hz screen.
- High pixel density multiplies area: a large 5K surface contains far more pixels to blend than a 720p test window.
- Layer count changes cost: many tiny promoted indicators can keep several surfaces active.
- Overlays change the equation: a fixed translucent layer may require composition over the entire viewport.
- DevTools changes the environment: debugging instrumentation can affect timing, so verify with the tools closed and an external process view where possible.
- Accessibility remains part of performance: respect
prefers-reduced-motionand provide a static path for users who request it.
A Better AI-Assisted Workflow for Weird Performance Bugs
- Write the symptom as a measurable contract. Record browser, OS, device, viewport, resolution, refresh rate, power state, page state, open windows, and the metric that must improve. "Laptop gets hot" is a clue; "isolated browser GPU process remains above 25% for 60 seconds on an idle 5K 120 Hz page" is testable.
- Isolate the owner of the work. Use a clean browser profile or separate browser with one tab. Compare another simple page. Close unrelated windows. Separate local harness processes from remote machines. Confirm whether CPU, GPU, network, memory, or disk changes with the target page.
- Collect more than one view. Chrome's Performance Monitor tracks CPU, heap, DOM nodes, listeners, layouts, and style recalculations. The Performance panel exposes frame and main-thread traces. Browser or OS task managers can reveal process-level behavior with DevTools closed. No single panel explains every subsystem.
- Ask the agent for an inventory, not a verdict. Search for infinite animations,
requestAnimationFrame, timers, videos, canvases, fixed overlays, filters, backdrop blur, large shadows, observers, background polling, WebSockets, and code that scales with history. Require file and selector references. - Generate reversible switches. Build a runtime helper or feature flags that disable one subsystem at a time. Include
applyAll()andreset(). The first goal is a large repeatable metric change, not a pretty interface. - Bisect interactions. After a category moves the metric, split it again. Test animation versus transition, one status family versus another, overlay present versus absent, 60 versus 120 Hz, low versus high resolution, and active versus idle thread state.
- Make the smallest product change that preserves intent. Duty-cycle or stop invisible motion, move overlays out of hot paths, reduce affected area, cache surfaces, and respect reduced-motion preferences. Avoid deleting useful feedback merely because the first model suggested it.
- Verify performance and appearance separately. Capture before and after metrics with a fixed protocol. Then inspect light and dark themes, SDR and HDR if relevant, mobile and desktop, active and idle states, and any seam or tint exposed by moving layers.
- Ship with an idle budget. Add a repeatable browser scenario or manual release check that opens the representative state, waits for settling, and records the process. Performance regressions often return because no acceptance gate represents the expensive environment.
Copy-Ready Diagnostic Prompt
Act as a performance investigation engineer.
Do not propose or implement a broad fix yet.
Symptom:
- Target metric: [CPU, GPU process, memory, FPS, battery, network]
- Baseline value: [measured value and duration]
- Environment: [device, OS, browser, viewport, resolution, refresh rate]
- Reproduction state: [exact page and active UI state]
- Control result: [same browser on a simple page]
Your job:
1. Separate hypotheses by subsystem: server, network, JS/main thread,
layout/paint, compositor/GPU, media, extensions, and other tabs.
2. For each hypothesis, name the observation that would falsify it.
3. Inventory all candidate code with file paths and selectors.
4. Build a reversible runtime diagnostic helper with:
- one toggle per subsystem,
- applyAll(), reset(), and currentState(),
- no persistent data changes,
- no production deployment.
5. Provide a fixed measurement protocol and results table.
6. Change one variable per run, then test likely interactions.
7. Distinguish observed evidence from inference.
8. Stop before editing product code and report the smallest causal set.
Acceptance gate:
- A candidate cause must produce a repeatable change in the target metric.
- A fix must preserve visual and functional behavior.
- Passing tests or a plausible explanation are not performance evidence.
This prompt deliberately prevents the model from rewarding itself for a large diff. The first deliverable is an instrumented question. Product code changes begin only after the evidence narrows the causal set.
Six Rules for Engineering Teams Using Coding Agents
| Rule | Why it matters |
|---|---|
| No large performance patch without a baseline and acceptance metric. | A big optimization can improve real code while completely missing the reported incident. |
| Agent explanations are hypotheses until a controlled test changes the metric. | Fluent narratives are cheap; causal evidence is the scarce output. |
| Keep diagnostic code separate and reversible. | Temporary CSS overrides, tracing hooks, and test scripts should answer questions without becoming accidental product architecture. |
| Measure idle behavior, not only load and interaction speed. | A responsive app can still consume energy, heat a laptop, and reduce battery life while nothing appears to happen. |
| Test the displays and browsers that amplify the failure. | 60 Hz, low-DPI development can hide a compositor problem that becomes obvious at 120 Hz and 5K. |
| Review visual regressions with the same seriousness as the metric. | The first optimization exposed gray seams and tint differences, especially on HDR hardware. A faster broken interface is not accepted work. |
Video Chapters
| Time | Section | What to watch for |
|---|---|---|
| 00:00 | The performance mystery | A responsive app with a browser GPU process consuming up to creator-reported 50% on the largest setup. |
| 02:29 | Blacksmith sponsor | Sponsored CI segment; separate from the T3 Code diagnosis. |
| 03:35 | How the problem surfaced | Heavy agent use, remote machines, and a laptop that remained hot after game streaming stopped. |
| 05:30 | Browser isolation | Why remote harness processes could not explain the local GPU load. |
| 08:10 | The confident wrong diagnosis | Sol optimizes WebSockets, React updates, history, persistence, and highlighting. |
| 09:45 | Switching from CPU to GPU | Refresh rate and display resolution become diagnostic clues. |
| 11:42 | Changing the agent's job | The model builds a console-controlled experiment harness instead of another speculative fix. |
| 13:56 | Animations implicated | Disabling animation collapses GPU-process usage. |
| 15:43 | The tiny pulsing icon | An ordinary status animation becomes the lead suspect. |
| 18:43 | Fable's animation inventory | Useful search mixed with more irrelevant suspects. |
| 20:13 | Blur, grain, and composition | The fixed noise layer and persistent motion form the expensive interaction. |
| 21:25 | Visual regressions | Removing grain and retinting surfaces exposes seams on HDR displays. |
| 23:12 | The second false alarm | Other idle Claude windows distort the browser-wide GPU measurement. |
| 24:56 | The engineering lesson | Agents search and build tools; experienced humans still steer ambiguous diagnosis. |
Bottom Line
T3 Code's bug is a compact lesson in why agentic engineering still needs engineering judgment. The agents found real code smells, generated substantial optimizations, inventoried animations, and built excellent diagnostic helpers. They also repeatedly overfit to the wrong explanation, suggested removing useful motion, and failed to preserve subtle visual behavior without human correction.
The winning division of labor was not human versus model. It was human causal reasoning plus machine-scale search and instrumentation. Theo supplied the surprising observations, rejected stories that did not predict them, and chose the next test. Sol and Fable accelerated code search, toggle generation, and implementation. That is a stronger model for serious debugging than asking an agent to "find and fix the performance problem" and trusting the first enormous pull request.
Sources
- Primary video: Theo - t3.gg, "Fable Broke My App and Couldn't Fix It", plus the attached transcript.
- Creator credit: Theo - t3.gg on YouTube and @theo on X.
- Project: pingdotgg/t3code on GitHub and T3 Code.
- CPU investigation: PR #3825, reduce CPU usage in event replay, projections, and sidebar.
- GPU fix and measurements: PR #3978, duty-cycle status animations and remove the fixed noise overlay.
- Live monitoring: Chrome DevTools Performance Monitor.
- Runtime diagnosis: Chrome DevTools guide to runtime performance.
- Animation mechanics: MDN, Animation performance and frame rate and web.dev, How to create high-performance CSS animations.
- Motion accessibility: MDN, prefers-reduced-motion.