Smash Story: How I Hunted Down a 45% Idle CPU Leak in an Interactive Retro Music App
This is a submission for DEV's Summer Bug Smash: Smash Stories powered by Sentry . The Scene of the Crime I was testing my retro music web app on my laptop when suddenly the fans started spinning up like a jet engine. The app is an interactive experience with cassette decks, MP3 players, and canvas-based visual effects (floating dust particles, CRT scanlines, and an audio visualizer). Visually,…
This is a story from the DEV Summer Bug Smash competition, featuring a report on how to track down a 45% CPU leak in a retro music web app. The app features interactive elements like cassette decks and visual effects, but the developer discovered the app was using excessive CPU resources even when idle. A direct link to the GitHub repository is provided.
The developer's investigation began with a noticeable fan noise from their laptop as they tested the app. They opened Chrome DevTools and noticed the CPU was stuck at 45% on an idle tab. Memory usage was also gradually increasing by 15MB per minute, even when switching to another browser tab. The developer suspected a React re-render loop but ruled that out by observing the React DevTools Profiler, which showed only one re-render every 500ms for a progress bar component - accounting for less than 2% of the CPU usage.
Next, the developer focused on isolating the canvas layers to pinpoint the source of the leak. Disabling the CRT filter reduced the CPU usage to ~38%, turning off the dust particles brought it down to ~25%, and disabling the audio visualizer dropped it to 1.2%. The problem was evident: uncontrolled requestAnimationFrame loops were running continuously, regardless of whether the music was paused or the tab was hidden.
Upon further analysis, the developer identified three main issues contributing to the leak: no tab visibility checks, allocations inside 60 FPS loops creating garbage collection overhead, and stacked animation frames that started without cancelling previous ones. To fix the leak, the developer refactored the canvas loops to pause when the tab was hidden or music was paused.
This involved using useEffect to render frames only when the tab is visible and audio is playing, as well as adding event listeners for visibilitychange to gracefully cancel background animation frames.
After implementing these changes, the developer observed significant improvements: idle CPU usage dropped from 45% down to just 0.8%, background tab CPU became completely suspended (0%), and memory usage remained flat with no more fan noise. The developer also shared their experience using Google AI (Gemini) during performance profiling and optimization.
By feeding flame chart profiles and canvas animation loops into Gemini, the AI pointed out the excessive creation of temporary color strings and object allocations inside a 60 FPS animation loop, which caused aggressive V8 garbage collection cycles. Gemini recommended binding a centralized document.addEventListener(visibilitychange) handler to freeze background animation frames when switching between browser tabs.
Written by urgent.news from Dev.to's reporting — not their text. Machine-written — may contain errors; check the original before relying on it.