A presentation at performance.now() in in Amsterdam, Netherlands by Andy Davies

Making sense of Long Animation Frames Andy Davies · #PerfNow · Oct 2025 @andydavies.me Photo by Patrick Fore on Unsplash
We’re shipping more and more JavaScript @andydavies.me
Download Size != impact document.addEventListener(“mousemove”, function() { for(var a = Date.now() + 500; Date.now() < a;) ; }); OK, so it’s a bit of a silly example but you get the idea… @andydavies.me
Runtime costs are a hidden danger @andydavies.me Photo by David Clode on Unsplash
Long Tasks API We’ve tried to measure them before… https://w3c.github.io/longtasks/ @andydavies.me
Long Task = Main Thread Task > 50ms @andydavies.me Captured at 4x slowdown in Chrome DevTools
Long Task = Main Thread Task > 50ms @andydavies.me Captured at 4x slowdown in Chrome DevTools
We can measure Long Tasks with RUM @andydavies.me SpeedCurve RUM
But we get no detail on their cause { “name”: “self”, “entryType”: “longtask”, “startTime”: 48723.60000002384, “duration”: 67, “navigationId”: “3ca0c548-7618-4423-87b7-41ae43415040”, “attribution”: [ { “name”: “unknown”, “entryType”: “taskattribution”, “startTime”: 0, “duration”: 0, “navigationId”: “3ca0c548-7618-4423-87b7-41ae43415040”, “containerType”: “window”, “containerSrc”: “”, “containerId”: “”, “containerName”: “” } ] } @andydavies.me
@andydavies.me Photo by Dušan veverkolog on Unsplash
How can Long Animation Frames help us? @andydavies.me Photo by Drew Hastings on Unsplash
Lo-AF @andydavies.me
Long Animation Frames (LoAF) ls Holidays Way Cars Multi-City Flight Flights Return Hotels One Way Holidays Cars Multi-City Flight Flights Return Hotels One Way Holidays Cars Multi-City Flight Flights Return Hotels One Way Holidays Cars Multi-City Flight Flights Return Hotels One Way Holidays Cars Multi-City Flight Flights Return Hotels One Way Hol M Fligh athrow (LHR) London, UK - Heathrow (LHR) London, UK - Heathrow (LHR) London, UK - Heathrow (LHR) London, UK - Heathrow (LHR) London, UK - Heathrow (LH Praia, Cape Verde, (RAI) Nelson Mandela, Praia, Cape Verde, (RAI) Nelson Mandela, Praia, Cape Verde, (RAI) Nelson Mandela, Praia, Cape Verde, (RAI) Nelson Mandela, Praia, Cape Verde, (RAI) Nelson Mandela, Praia, Cap 14th Nov Sat, 1st Nov – Sat 14th Nov Sat, 1st Nov – Sat 14th Nov Sat, 1st Nov – Sat 14th Nov Sat, 1st Nov – Sat 14th Nov Sat, 1st Nov – Sat 14th Nov 2 Adults, First 2 Adults, First 2 Adults, First 2 Adults, First 2 Adults, First Search Search @andydavies.me Search Search Search Search
in Cars Hotels ai Return One Way P Multi-City ght Fligh ow (LHR) a, Cape Verde, (RAI) h Flights nt Holidays g St ar ts Partial measurement of frame duration London, UK - Heathrow ( LoAF Nov earch @andydavies.me Nelson Mandela, Praia, C Sat, 1st Nov – Sat 14th No
50ms 2 Adults, First Searc
ed se in P ai P
50ms re nt e am Fr LoAF @andydavies.me nt g St ar St t ar ts Start of frame to start of Painting
LoAF ed se re P P Fr ai am nt e in nt g St ar St t ar ts Start of frame to start of Painting Paint Commit GPU @andydavies.me
@andydavies.me LoAF st st tT + t on ar ti St ra ut du yo t La e nd im eA ar yl ar e St im er tT nd ar re st Timestamps for key milestones
e st ar tT im nd eA yl st er nd re LoAF + yo La t ar St e im tT ar st Script Execution du ut ra St ti ar t on In simplified Main Thread terms… Style & Layout Paint Commit GPU @andydavies.me
e st ar tT im nd eA st yl er nd re LoAF + yo La t ar St e im tT ar st Script Execution du ut ra St ti ar t on Scripts execute during render phase too Style & Layout Paint Commit GPU requestAnimationFrame ResizeObserver IntersectionObserver AnimationEvents @andydavies.me
e st ar tT im nd eA yl st er nd re LoAF + yo La t ar St e im tT ar st Script Execution du ut ra St ti ar t on Warning: This is a simplified view! Style & Layout Paint Commit GPU • • @andydavies.me scripts can force Style & Layout browsers sometimes perform Style and Layout mid-frame
Need a refresher on the Event Loop? In the Loop, Jake Archibald https://vimeo.com/254947206
Paint Timing Mixin ta en es pr pa in tT im e ti on Ti m Adds paintTime & presentationTime – Chrome Canary only ATM LoAF https://www.w3.org/TR/paint-timing/#the-paint-timing-mixin @andydavies.me
Work @andydavies.me m pr st Style & Layout Ti en es tT ar yl st LoAF Pre-Style & Layout on ti ta e im nd eA er nd re + yo La t ar St e im tT ar st du ut ra St ti ar t on Calculate durations of internal phases Paint & Compositing
LoAFs can occur across the page lifecycle @andydavies.me Fast 4G, 4x CPU slowdown – https://trace.cafe/t/JcNXq2wneo
Normally at least one before FCP @andydavies.me
Often a LoAF intersecting DCL @andydavies.me
Often a LoAF intersecting DCL Currently… deferred scripts execute in a single task just before DCL (this may change) @andydavies.me
@andydavies.me SpeedCurve RUM
@andydavies.me SpeedCurve RUM
How do LoAFs affect visitor behaviour? @andydavies.me SpeedCurve RUM
Pages can generate many LoAF entries… 100% 75% Desktop Mobile Tablet 50% 25% 0% 0 20 40 60 80 100 120 140 160 180 200 220 240 LoAFs per Page @andydavies.me SpeedCurve RUM
Pages can generate many LoAF entries… 100% 75% Desktop Mobile Tablet 50% 25% 25 0% 0 20 40 60 80 100 120 140 160 180 200 220 240 LoAFs per Page @andydavies.me SpeedCurve RUM
Pages can generate many LoAF entries… 100% 75% Desktop Mobile Tablet 50% 25% 25 0% 0 20 40 60 60 80 100 120 140 160 180 200 220 240 LoAFs per Page @andydavies.me SpeedCurve RUM
Pages can generate many LoAF entries… 100% 75% Desktop Mobile Tablet 50% 25% 25 0% 0 20 40 60 60 100 80 100 120 140 160 180 200 220 240 LoAFs per Page @andydavies.me SpeedCurve RUM
Query entries from Performance Timeline performance.getEntriesByType(“long-animation-frame”); @andydavies.me
Observe entries via PerformanceObserver const observer = new PerformanceObserver((list) => { for (const entry of list.getEntries()) { console.log(entry) } }); observer.observe({ type: ‘long-animation-frame’, buffered: true }); @andydavies.me
Available in Chromium based browsers @andydavies.me
Entries are only created for visible tabs @andydavies.me
pr fir st es U en IE ta ve ti nt on Ti Ti m m e eS ta m p Did the frame handle an interaction? LoAF @andydavies.me
pr fir st es U en IE ta ve ti nt on Ti Ti m m e eS ta m p Did the frame handle an interaction? LoAF Interaction to Next Paint Measures a wider range of interactions than INP, but also has less detail @andydavies.me
How large could Input Delay be? FCP @andydavies.me
How large could Input Delay be? If someone clicks at this point in time It won’t be handled until at least here Longest LoAF = Maximum Potential Input Delay @andydavies.me
@andydavies.me st st tT + t on ar ti St ra ut du yo t La e nd im eA ar yl ar e St im er tT nd ar re st Estimate responsiveness to User Input
@andydavies.me st st tT + t on ar ti St ra ut du yo t La e nd im eA ar yl ar e St im er tT nd ar re st Estimate responsiveness to User Input
@andydavies.me st st tT + t on ar ti St ra ut du yo t La e nd im eA ar yl ar e St im er tT nd ar re st Estimate responsiveness to User Input
e st ar tT im nd eA yl st er nd re + yo La t ar St e im tT ar st blockingDuration @andydavies.me du ut ra St ti ar t on Estimate responsiveness to User Input
Total Blocking Duration (TBD) totalBlockingDuration = 0; LoAFs .forEach(entry => { totalBlockingDuration += entry.blockingDuration }); It’s a slightly naive approach but we launched TBD in SpeedCurve as a experiment with the aiming of learning and refining in public
Seems to match Main Thread busyness ok @andydavies.me SpeedCurve RUM
Refining Total Blocking Duration FCP @andydavies.me
Exclude LoAFs linked to interactions FCP @andydavies.me
What about LoAFs before FCP? FCP @andydavies.me @andydavies.me
What about LoAFs before FCP? FCP @andydavies.me
What about LoAFs before FCP? FCP
What about LoAFs before FCP? Could have rendered at this point FCP
What about LoAFs before FCP? Could have rendered at this point FCP Currently thinking of just removing the first LoAF but… …really interested in what every one else thinks too @andydavies.me
Updated Total Block Duration calculation FCP
Updated Total Block Duration calculation totalBlockingDuration = 0; LoAFs .slice(1) .filter(entry => entry.firstUIEventTimestamp == 0) .forEach(entry => { totalBlockingDuration += entry.blockingDuration }); @andydavies.me
Default thresholds need reviewing too @andydavies.me SpeedCurve RUM
LoAF has some novel uses… Photo by Jon Tyson on Unsplash
Approximate Frame Rate Calculations LoAF LoAF LoAF FPS = 1,000ms / LoAF.duration @andydavies.me LoAF
Approximate Frame Rate Calculations LoAF LoAF LoAF FPS = > 20 @andydavies.me LoAF
LoAF gives us useful data • How many frames were delayed and how long for? • Where did the frames spend their time? • What was the longest time a visitor might wait for a response? • How long was the main thread unable to respond to user input? • What was the approximate frame rate? • And more… @andydavies.me
It can help understand JS runtime costs @andydavies.me Photo by Jay Heike on Unsplash
Example LoAF entry { “name”: “long-animation-frame”, “entryType”: “long-animation-frame”, “navigationId”: “95558f7f-38d7-4a06-b15e-67ccf1dec62e”, “startTime”: 300.39999997615814, “duration”: 1573.4000000357628, “blockingDuration”: 605.986, “renderStart”: 1540, “styleAndLayoutStart”: 1540.1000000238419, “paintTime”: 1873.800000011921, “firstUIEventTimestamp”: 0, “scripts”: [] }, @andydavies.me
Script Execution @andydavies.me e st ar tT im nd eA yl st er nd re + yo La t ar St e im tT ar st LoAF du ut ra St ti ar t on Entry for each script execution > 5ms
e nS st ex ar ec tT ut im io e im tT ar st + ta du rt * ra ti on Entry for each script execution > 5ms Script Execution *executionStart is ‘muted’ for Cross Origin scripts @andydavies.me
e nS st ex ar ec tT ut im io e im tT ar st + ta du rt * ra ti on Entry for each script execution > 5ms Script Execution Compile Duration *executionStart is ‘muted’ for Cross Origin scripts @andydavies.me
Other timing information forcedStyleAndLayoutDuration getComputedStyle & getBoundingClientRect for the browser to recalculate Style and Layout pauseDuration Synchronous XHR can force the browser to pause @andydavies.me
What script executed? “sourceURL”: “https://www.example.com/script.js”, “sourceFunctionName”: “M”, “sourceCharPosition”: 98556 ! @andydavies.me
What script executed? “sourceURL”: “https://www.example.com/script.js”, “sourceFunctionName”: “M”, “sourceCharPosition”: 98556 Top level entry point ! @andydavies.me
Why did it execute? “invoker”: “DOMWindow.onclick”, “invokerType”: “event-listener”, @andydavies.me
Aggregate the Data… Roll up the Data… sourceURL https://www.selfridges.com/static-mfe-clp/_next/static/chunks/914-ff512dbd6d857625.js https://js-cdn.dynatrace.com/jstag/164ae1b51de/bf67380nlf/fb4fc3f4b31ef9b_complete.js https://www.googletagmanager.com/gtag/js?id=AW-989335448&l=gDatalayer&cx=c>m=4e5am0 https://www.selfridges.com/static-mfe-clp/_next/static/chunks/937-c796c646a443351b.js https://cdn.quantummetric.com/qscripts/quantum-selfridges.js https://www.selfridges.com/static-mfe-clp/_next/static/chunks/d8ec93b9-980f704a97712473.js https://www.googletagmanager.com/gtag/js?id=DC-5921516&l=gDatalayer&cx=c>m=4e5am0 https://tags.tiqcdn.com/utag/selfridges/main/prod/utag.js https://tags.pw.adn.cloud/SMVEJB/activation.js https://f.monetate.net/trk/4/s/a-26b02505/p/selfridges.com/1584236768-0? mr=t1640009934&mi=%272.1452019399.1731666309291%27&cs=!t&e=! https://www.googletagmanager.com/gtag/js?id=G-R05V82D63H&l=gDatalayer https://www.selfridges.com/static-mfe-clp/_next/static/chunks/1dd3208c-191771bcb3be71a1.js https://tags.tiqcdn.com/utag/selfridges/main/prod/utag.416.js?utv=ut4.51.202210200808 https://se.monetate.net/js/2/a-26b02505/p/selfridges.com/custom.js https://analytics.tiktok.com/i18n/pixel/static/main.MWI5ZTBkMTQ4MA.js https://www.selfridges.com/GB/en/ https://cdn.attraqt.io/xo.all-2.min.js https://sb.monetate.net/img/1/p/1581/5347147.js/monetate.c.cr.js https://static.queue-it.net/script/queueconfigloader.min.js https://www.selfridges.com/static-mfe-clp/_next/static/chunks/61721e05-4efc862ff71175ec.js Total Duration (ms) Total ForcedStyleAndLayout Duration (ms) Occurrences 4,555 1,328 1,021 938 842 767 618 543 226 204 156 131 60 57 38 29 21 7 6 5 5 139 8 0 0 0 0 0 119 0 0 0 0 0 0 0 0 0 0 0 0 0 30 18 2 1 10 3 1 5 2 1 2 6 1 2 3 1 1 1 1 1 1
Aggregate the Data… Roll up the Data… sourceURL https://www.selfridges.com/static-mfe-clp/_next/static/chunks/914-ff512dbd6d857625.js https://js-cdn.dynatrace.com/jstag/164ae1b51de/bf67380nlf/fb4fc3f4b31ef9b_complete.js https://www.googletagmanager.com/gtag/js?id=AW-989335448&l=gDatalayer&cx=c>m=4e5am0 https://www.selfridges.com/static-mfe-clp/_next/static/chunks/937-c796c646a443351b.js https://cdn.quantummetric.com/qscripts/quantum-selfridges.js https://www.selfridges.com/static-mfe-clp/_next/static/chunks/d8ec93b9-980f704a97712473.js https://www.googletagmanager.com/gtag/js?id=DC-5921516&l=gDatalayer&cx=c>m=4e5am0 https://tags.tiqcdn.com/utag/selfridges/main/prod/utag.js https://tags.pw.adn.cloud/SMVEJB/activation.js https://f.monetate.net/trk/4/s/a-26b02505/p/selfridges.com/1584236768-0? mr=t1640009934&mi=%272.1452019399.1731666309291%27&cs=!t&e=! https://www.googletagmanager.com/gtag/js?id=G-R05V82D63H&l=gDatalayer https://www.selfridges.com/static-mfe-clp/_next/static/chunks/1dd3208c-191771bcb3be71a1.js https://tags.tiqcdn.com/utag/selfridges/main/prod/utag.416.js?utv=ut4.51.202210200808 https://se.monetate.net/js/2/a-26b02505/p/selfridges.com/custom.js https://analytics.tiktok.com/i18n/pixel/static/main.MWI5ZTBkMTQ4MA.js https://www.selfridges.com/GB/en/ https://cdn.attraqt.io/xo.all-2.min.js https://sb.monetate.net/img/1/p/1581/5347147.js/monetate.c.cr.js https://static.queue-it.net/script/queueconfigloader.min.js https://www.selfridges.com/static-mfe-clp/_next/static/chunks/61721e05-4efc862ff71175ec.js Total Duration (ms) Total ForcedStyleAndLayout Duration (ms) Occurrences 4,555 1,328 1,021 938 842 767 618 543 226 204 156 131 60 57 38 29 21 7 6 5 5 139 8 0 0 0 0 0 119 0 0 0 0 0 0 0 0 0 0 0 0 0 30 18 2 1 10 3 1 5 2 1 2 6 1 2 3 1 1 1 1 1 1
Are 3rd-party tags really the problem? Total Script Duration (ms) @andydavies.me 1st Party 3rd Party 6,446 5,111 Captured at 4x slowdown in Chrome DevTools
May generate many entries per page CPU Slowdown 0x 4x 6x LOAF Entries 21 32 37 ScriptTiming Entries 31 39 47 Example only: @andydavies.me Real world values will depend on device CPU, network connectivity and duration of observation
Helps answer questions such as • Which scripts have the most impact on visitors experience? • Which scripts are forcing style and layout operations? • Which scripts delay FCP or LCP or affect a visitor completing a task? • Are my 1st Party or 3rd-party scripts a problem? @andydavies.me
LoAF can help diagnose slow interactions @andydavies.me Photo by Nik on Unsplash
Interaction to Next Paint (INP) Time between a visitor interacting and the next frame being presented Flights Return Hotels Holidays One Way Cars Multi-City Return Hotels One Way Holidays Cars Multi-City London, UK - Heathrow (LHR) London, UK - Heathrow (LHR) Nelson Mandela, Praia, Cape Verde, (RAI) Nelson Mandela, Praia, Cape Verde, (RAI) Sat, 1st Nov – Sat 14th Nov Sat, 1st Nov – Sat 14th Nov 2 Adults, First 2 Adults, First Flight Search @andydavies.me Flights Flight
INP has Three Phases Input Delay Waiting for event handler to execute @andydavies.me Processing Duration Presentation Delay Event handler execution Waiting for new frame to be presented
INP has Three Phases Input Delay Other Tasks @andydavies.me Processing Duration Presentation Delay Tasks related to the event handler
INP has Three Phases Input Delay Other Tasks Processing Duration Presentation Delay Tasks related to the event handler Can use DevTools to debug some slow interactions @andydavies.me
INP has Three Phases Input Delay Processing Duration Other Tasks But this depends on what’s executing when someone interacts @andydavies.me Presentation Delay Tasks related to the event handler
@andydavies.me SpeedCurve RUM
No direct way to link EventTiming & LoAF Photo by GeoJango Maps on Unsplash
Use timestamps to map scripts to phases Input Delay Processing Duration LoAF JS @andydavies.me Presentation Delay LoAF JS JS JS LoAF JS JS JS
Discard LoAFs ending after Presentation Input Delay Processing Duration LoAF JS Presentation Delay LoAF JS JS JS LoAF JS JS JS Tasks in next frame can execute once current frame is sent to GPU @andydavies.me
Discard LoAFs ending after Presentation Input Delay Processing Duration LoAF JS @andydavies.me LoAF JS JS JS JS Presentation Delay
Exclude any script execution before Input Input Delay Processing Duration LoAF JS LoAF JS Script started executing before visitor interacted JS JS JS Presentation Delay
Exclude script execution before Input Input Delay JS Processing Duration LoAF JS JS @andydavies.me LoAF JS JS JS JS Presentation Delay
Assign scripts that are left to INP phases Input Delay JS JS JS Processing Duration LoAF LoAF JS ! @andydavies.me JS JS JS Presentation Delay
Assign scripts that are left to INP phases phase InputDelay InputDelay InputDelay InputDelay ProcessingTime @andydavies.me invokerType classic-script event-listener event-listener event-listener event-listener duration (ms) 119 10 5 12 12 sourceURL https://tags.tiqcdn.com/utag/selfridges/main/prod/utag.js https://js-cdn.dynatrace.com/jstag/164ae1b51de/bf67380nlf/fb4fc3f4b31ef https://www.selfridges.com/NL/en/features/etc/designs/zg/selfridges-new/e https://www.selfridges.com/NL/en/features/etc/designs/zg/selfridges-new/e
Can help us answer questions such as: • How are scripts affecting our visitors interactions? • Which scripts have have the most impact across all interactions? • Which are the slowest interaction handlers? • Which scripts commonly delay interaction handlers @andydavies.me
Attribution Issues: Empty sourceFunction phase ID ID ID ID PT invoker invokerType entryPoint duration (ms) sourceURL https://tags.tiqcdn.com/utag/selfridges/main/prod/utag.js classic-script 119 https://tags.tiqcdn.com/utag/selfridges/main/p SCRIPT[src=//tags.tiqcdn.com/utag/selfridges/main/prod/utag.js].onload event-listener 10 #document.ontouchstart event-listener 5 https://js-cdn.dynatrace.com/jstag/164ae1b51 #document.ontouchstart event-listener 12 https://www.selfridges.com/NL/en/features/etc BODY#selfridges-app.onclick event-listener 12 https://www.selfridges.com/NL/en/features/etc Entry points may be empty or minified function names @andydavies.me
Attribution Issues: Empty sourceURLs phase ID ID ID ID PT invoker invokerType entryPoint duration (ms) sourceURL https://tags.tiqcdn.com/utag/selfridges/main/prod/utag.js classic-script 119 https://tags.tiqcdn.com/utag/selfridges/main/p SCRIPT[src=//tags.tiqcdn.com/utag/selfridges/main/prod/utag.js].onload event-listener 10 #document.ontouchstart event-listener 5 https://js-cdn.dynatrace.com/jstag/164ae1b51 #document.ontouchstart event-listener 12 https://www.selfridges.com/NL/en/features/etc BODY#selfridges-app.onclick event-listener 12 https://www.selfridges.com/NL/en/features/etc @andydavies.me Sometimes sourceURLs are empty
Attribution Issues: Wrappers invoker invokerType sourceFunctionName sourceURL Total Duration Occurences #document.ontouchend event-listener nrWrapper …com/browser-performance/newrelic.js 13 1 svg.onclick event-listener nrWrapper …com/browser-performance/newrelic.js 16 2 DOMWindow.onmousemove event-listener nrWrapper …com/browser-performance/newrelic.js 8 1 #document.onmousedown event-listener nrWrapper …com/browser-performance/newrelic.js 10 1 #document.onpointerdown event-listener nrWrapper …com/browser-performance/newrelic.js 28 2 DOMWindow.onclick event-listener nrWrapper …com/browser-performance/newrelic.js 136 1 A.onclick event-listener nrWrapper …com/browser-performance/newrelic.js 10 2 BODY.onclick event-listener nrWrapper …com/browser-performance/newrelic.js 26 2 #document.ontouchstart event-listener nrWrapper …com/browser-performance/newrelic.js 13 1 #document.onclick event-listener nrWrapper …com/browser-performance/newrelic.js 14 2 DIV#root.onclick event-listener nrWrapper …com/browser-performance/newrelic.js 167 1
Attribution Issues: Versioned scripts Versioned / hashed resources are common in modern build processes …/_next/static/chunks/1dd3208c-191771bcb3be71a1.js …/_next/static/chunks/914-ff512dbd6d857625.js …/_next/static/chunks/937-c796c646a443351b.js They can make it hard to track resource performance over time @andydavies.me
(program: 2) @andydavies.me
Name sources using //# sourceURL <script type=”text/javascript”> document.body.addEventListener(“click”, function(){ for(var a=Date.now()+2E3;Date.now()<a;); }); //# sourceURL=Use-the-sourceURL-Luke </script> Will be used for sourceURL, works with external scripts too Example: https://andydavies.github.io/agent-tests/loaf/sourceurl.html @andydavies.me
No Script Entries for • Extensions For privacy reasons we can’t know what extensions a visitor is using Chrome on Android doesn’t support extensions • Garbage Collection Tickets open for opaque attribution of these @andydavies.me
https://webperf-snippets.nucliweb.net/Interaction/Long-Animation-Frames-Helpers
https://github.com/andydavies/perf-timeline-to-devtools-profile
I ♥ LoAF
LoAF provides insights into the runtime costs of the code we ship And allow us to identify our problem scripts Within the context of our visitors environment @andydavies.me
You’re going to need RUM Photo by Ash Edmonds on Unsplash
Feel like I’ve just scratched the surface… … and there’s more for LoAF to reveal @andydavies.me Photo by Mat Ranson on Unsplash
Finally… if you’re flying out of Schipol… Photo by Erwin Hofman
Thanks! @andydavies.me andy.davies@speedcurve.com
Over time the web has become more reliant on JavaScript… but we’ve very little information on how its growth is affecting our visitors’ experience.
Some believe JavaScript is the only way to build the modern web and dismiss the performance concerns, while others hold opposing views.
Wouldn’t it be great if we had data on how scripts actually perform in our visitor’s browsers – when they delay loading, cause jankiness and slowdown interactions, and perhaps more importantly which scripts are responsible?
After all, if we can’t measure it, how can we improve it?
The Long Tasks API was a first attempt at providing this data but it only helped us to understand when the Main Thread was busy and couldn’t answer the question of why it was busy.
The Long Animation Frames (LoAF) API aims to overcome the weaknesses of the Long Tasks API and provides both information on when there were long frames during rendering the process, and where possible, details on what Main Thread activity caused the long frame.
In this talk we’ll explore:
What data the API provides and how it relates to the Main Thread activity we see when profiling pages using Chrome DevTools
How we can use this data to get a better understanding of our visitors experience in the wild and identify scripts that are having a harmful impact on our visitors experience
What data the API doesn’t currently provide and how some of these gaps might be filled in the future
At the end of this talk you’ll have a good understanding of the Long Animation Frames API and how to use the data to identify scripts that need improvement.
We’ll also go on a few side quests to explore some of the other uses of LoAF data too.