Browser video analytics: ditch timeupdate for requestVideoFrameCallback
The CourseSpeed developer explains why the timeupdate event is unreliable for video analytics and how requestVideoFrameCallback offers a battery-efficient alternative.
Frustrated by inaccurate progress tracking on e-learning platforms, a developer built a browser extension called CourseSpeed to get granular video engagement analytics. Along the way, they discovered that the widely-used timeupdate event is a poor foundation for this kind of work: browsers fire it at unpredictable rates—anywhere from 4 to 60 times per second—forcing heavy analytics computations onto the main thread at random intervals, which causes playback stutters and drains battery on mobile devices.
The fix is requestVideoFrameCallback (rVFC), introduced in Chrome 83 and now supported across Chromium-based browsers and Safari. Rather than tying into a generic timer-like event, rVFC hooks directly into the browser's rendering pipeline and fires exactly once per rendered frame, also exposing metadata like mediaTime and presentedFrames that make it possible to detect dropped frames or buffering issues. After migrating CourseSpeed's tracking loop to rVFC, the author measured roughly a 14% drop in CPU usage during a two-hour course on an M1 MacBook.
The piece also tackles the practical challenge of locating the actual <video> element across platforms that bury it inside nested iframes (Udemy), complex React component trees (Coursera), or shadow DOM structures (Skillshare). A recursive helper function that pierces same-origin iframes and open shadow roots is shared as a solution. Since Firefox doesn't yet support rVFC, a fallback using requestAnimationFrame combined with currentTime checks is recommended as a still-superior alternative to timeupdate.
The broader takeaway for engineers building browser extensions or custom video players: anchoring your logic to low-level rendering APIs rather than high-level DOM events yields smoother, more battery-efficient, and more accurate results.