Interaction
Long Animation Frames

Long Animation Frames

This snippet requires specific features that are either available in Chrome 115+ for origins that are part of the LoAF Origin Trial (opens in a new tab) or require that you enable the #enable-experimental-web-platform-features flag in chrome://flags (please note that you will need to restart your browser).

To determine when long animation frames (LoAF) happen, you can use PerformanceObserver (opens in a new tab) and register to observe entries of type long-animation-frame. This snippet from @noamr (opens in a new tab) provides additional informations computed from the LoAF raw data.

Snippet

(function init() {  function processAndFilterLoAFs(entries) {  function floorObject(o) {  return Object.fromEntries(Array.from(Object.entries(o)).map(([key, value]) =>  [key, typeof value === "number" ? Math.floor(value) :  value]))  }    function processEntry(entry) {  const startTime = entry.startTime;  const endTime = entry.startTime + entry.duration;  const delay = entry.desiredRenderStart ? Math.max(0, entry.startTime - entry.desiredRenderStart) : 0;  const deferredDuration = Math.max(0, entry.desiredRenderStart - entry.startTime);  const renderDuration = entry.styleAndLayoutStart - entry.renderStart;  const workDuration = entry.renderStart ? entry.renderStart - entry.startTime : entry.duration;  const totalForcedStyleAndLayoutDuration = entry.scripts.reduce((sum, script) => sum + script.forcedStyleAndLayoutDuration, 0);  const styleAndLayoutDuration = entry.styleAndLayoutStart ? endTime - entry.styleAndLayoutStart : 0;  const scripts = entry.scripts.map(script => {  const delay = script.startTime - script.desiredExecutionStart;  const scriptEnd = script.startTime + script.duration;  const compileDuration = script.executionStart - script.startTime;  const execDuration = scriptEnd - script.executionStart;  return floorObject({delay, compileDuration, execDuration, ...script.toJSON()});  })  return floorObject({startTime, delay, deferredDuration, renderDuration, workDuration, styleAndLayoutDuration, totalForcedStyleAndLayoutDuration, ...entry.toJSON(), scripts});  }    return entries.map(processEntry);  }    function analyze() {  return loafs.map(loaf => (  {  blockingDuration: loaf.blockingDuration,  loaf,  scripts: loaf.scripts, events: events.filter(e => overlap(e, loaf))  })).filter(l => (l.blockingDuration && l.events.length));  }        let loafs = [];  let events = [];  function processLoAFs(entries) {  loafs = [...loafs, ...processAndFilterLoAFs(entries.getEntries())];  console.log(analyze());  }    function processEvents(entries) {  events = [...events, ...entries.getEntries()];  console.log(analyze());  }  new PerformanceObserver(processLoAFs).observe(  {type: "long-animation-frame", buffered:true});  new PerformanceObserver(processEvents).observe(  {type: "event", buffered:true});    function overlap(e1, e2) {  return e1.startTime < (e2.startTime + e2.duration) &&  e2.startTime < (e1.startTime + e1.duration)  }  window.whynp = analyze;  } )()