[{"data":1,"prerenderedAt":1746},["ShallowReactive",2],{"content:\u002Fcore-web-vitals-measurement\u002Flong-animation-frames-api\u002Ffinding-slow-scripts-with-loaf-script-attribution":3,"surroundings:\u002Fcore-web-vitals-measurement\u002Flong-animation-frames-api\u002Ffinding-slow-scripts-with-loaf-script-attribution":1738},{"id":4,"title":5,"body":6,"description":1717,"extension":1718,"meta":1719,"navigation":1732,"path":1733,"seo":1734,"stem":1736,"__hash__":1737},"content\u002Fcore-web-vitals-measurement\u002Flong-animation-frames-api\u002Ffinding-slow-scripts-with-loaf-script-attribution\u002Findex.md","Finding Slow Scripts with LoAF Script Attribution",{"type":7,"value":8,"toc":1703},"minimark",[9,14,33,45,176,181,257,261,273,290,303,326,415,419,424,427,649,652,656,659,963,966,970,979,1251,1254,1258,1304,1307,1416,1420,1427,1544,1548,1551,1576,1592,1605,1618,1621,1625,1637,1661,1665,1688,1693,1696,1699],[10,11,13],"h1",{"id":12},"how-to-find-the-script-behind-a-slow-interaction-with-loaf-attribution","How to Find the Script Behind a Slow Interaction with LoAF Attribution",[15,16,17,18,23,24,28,29],"p",{},"This guide applies the ",[19,20,22],"a",{"href":21},"\u002Fcore-web-vitals-measurement\u002Flong-animation-frames-api\u002F","Long Animation Frames API"," to the most common question it answers, within the wider ",[19,25,27],{"href":26},"\u002Fcore-web-vitals-measurement\u002F","Core Web Vitals & Measurement"," section: ",[30,31,32],"em",{},"which code made this interaction miss the 200ms budget?",[15,34,35,36,40,41,44],{},"The scenario: RUM says INP is ",[37,38,39],"code",{},"280ms"," at p75 on your product listing page, and the Performance panel shows a dense wall of yellow when you click a filter. Every script on the page has an owner, but nobody can say whose code is in that wall. LoAF's ",[37,42,43],{},"scripts"," array can, provided you read it the right way — ranked by duration, grouped by source, and resolved through source maps.",[15,46,47],{},[48,49,55,56,55,63,55,67,55,70,55,88,55,96,55,102,55,110,55,117,55,122,55,126,55,129,55,133,55,136,55,142,55,146,55,149,55,152,55,156,55,159,55,168,55,172,55],"svg",{"viewBox":50,"width":51,"role":52,"ariaLabel":53,"style":54},"0 0 760 149","100%","img","Four steps from collecting long animation frames to producing a ranked, owned list of slow scripts.","height:auto;max-width:760px;display:block;margin:1.75rem auto;font-family:inherit;color:var(--fp-svg-ink)"," ",[57,58],"rect",{"className":59,"x":61,"y":61,"width":51,"height":51,"fill":62},[60],"svg-canvas","0","#ffffff",[64,65,66],"title",{},"From raw frame to owned ticket",[68,69,53],"desc",{},[71,72,73],"defs",{},[74,75,82],"marker",{"id":76,"viewBox":77,"refX":78,"refY":79,"markerWidth":80,"markerHeight":80,"orient":81},"fa7bb296f3","0 0 10 10","9","5","7","auto-start-reverse",[83,84],"path",{"d":85,"fill":86,"style":87},"M0 0 L10 5 L0 10 z","currentColor","fill-opacity:0.7",[57,89],{"x":90,"y":90,"width":91,"height":92,"rx":93,"fill":94,"stroke":86,"style":95},"1","758","147","10","none","stroke-opacity:0.18",[97,98,66],"text",{"x":99,"y":100,"fill":86,"style":101},"28.0","34.0","font-size:16px;font-weight:700",[57,103],{"x":99,"y":104,"width":105,"height":106,"rx":107,"fill":108,"stroke":108,"style":109},"56.0","146.0","71.0","6","#0466c8","fill-opacity:0.14;stroke-opacity:0.9",[97,111,116],{"x":112,"y":113,"fill":86,"style":114,"textAnchor":115},"101.0","78.0","font-size:13px;font-weight:700","middle","Collect frames",[97,118,121],{"x":112,"y":119,"fill":86,"style":120,"textAnchor":115},"96.0","font-size:12px","interactive and over",[97,123,125],{"x":112,"y":124,"fill":86,"style":120,"textAnchor":115},"112.0","50ms",[57,127],{"x":128,"y":104,"width":105,"height":106,"rx":107,"fill":108,"stroke":108,"style":109},"214.0",[97,130,132],{"x":131,"y":113,"fill":86,"style":114,"textAnchor":115},"287.0","Flatten scripts",[97,134,135],{"x":131,"y":119,"fill":86,"style":120,"textAnchor":115},"one row per script entry",[57,137],{"x":138,"y":104,"width":105,"height":106,"rx":107,"fill":139,"stroke":140,"style":141},"400.0","#ffc300","#b8860b","fill-opacity:0.24;stroke-opacity:0.9",[97,143,145],{"x":144,"y":113,"fill":86,"style":114,"textAnchor":115},"473.0","Group and rank",[97,147,148],{"x":144,"y":119,"fill":86,"style":120,"textAnchor":115},"by URL and function",[57,150],{"x":151,"y":104,"width":105,"height":106,"rx":107,"fill":108,"stroke":108,"style":109},"586.0",[97,153,155],{"x":154,"y":113,"fill":86,"style":114,"textAnchor":115},"659.0","Source-map",[97,157,158],{"x":154,"y":119,"fill":86,"style":120,"textAnchor":115},"file",[160,161],"line",{"x1":162,"y1":163,"x2":164,"y2":163,"stroke":86,"strokeWidth":165,"style":166,"markerEnd":167},"174.0","91.5","212.0","1.5","stroke-opacity:0.6","url(#fa7bb296f3)",[160,169],{"x1":170,"y1":163,"x2":171,"y2":163,"stroke":86,"strokeWidth":165,"style":166,"markerEnd":167},"360.0","398.0",[160,173],{"x1":174,"y1":163,"x2":175,"y2":163,"stroke":86,"strokeWidth":165,"style":166,"markerEnd":167},"546.0","584.0",[177,178,180],"h2",{"id":179},"rapid-diagnosis","Rapid Diagnosis",[182,183,184,196,202,220,233],"ul",{},[185,186,187,191,192,195],"li",{},[188,189,190],"strong",{},"Confirm the frame is interactive."," In the console, read the frame's ",[37,193,194],{},"firstUIEventTimestamp","; zero means no input was processed in it and it is not your INP frame.",[185,197,198,201],{},[188,199,200],{},"Count the scripts."," One dominant script entry points to a single handler; ten mid-sized entries point to a fan-out or several listeners on one event.",[185,203,204,211,212,215,216,219],{},[188,205,206,207,210],{},"Check ",[37,208,209],{},"sourceFunctionName","."," Blank names with a ",[37,213,214],{},"sourceCharPosition"," of ",[37,217,218],{},"-1"," mean a cross-origin script without CORS — you can identify the vendor but not the function.",[185,221,222,232],{},[188,223,224,225,228,229,210],{},"Compare ",[37,226,227],{},"duration"," with ",[37,230,231],{},"forcedStyleAndLayoutDuration"," If layout is most of a script's time, the code is not slow; its DOM access pattern is.",[185,234,235,55,241,244,245,248,249,252,253,256],{},[188,236,237,238,210],{},"Note the ",[37,239,240],{},"invokerType",[37,242,243],{},"event-listener"," is your handler; ",[37,246,247],{},"resolve-promise"," is work chained after an ",[37,250,251],{},"await","; ",[37,254,255],{},"classic-script"," is a script evaluating mid-interaction.",[177,258,260],{"id":259},"root-cause-analysis","Root Cause Analysis",[15,262,263,266,267,269,270,272],{},[188,264,265],{},"1. A single heavy listener."," One ",[37,268,243],{}," entry exceeds ",[37,271,125],{},". The handler does synchronous work — sorting, filtering, serialising — before returning. This is the easiest case to fix and the most common in first-party code.",[15,274,275,278,279,282,283,285,286,289],{},[188,276,277],{},"2. A promise chain that outlives the listener."," The listener itself is ",[37,280,281],{},"5ms",", but ",[37,284,247],{}," entries from the same source file total ",[37,287,288],{},"120ms",". The code awaited a fetch or a microtask and then did the expensive part. Developers miss this because the handler looks innocent in review.",[15,291,292,295,296,298,299,302],{},[188,293,294],{},"3. A vendor script evaluating on the same frame."," A ",[37,297,255],{}," or ",[37,300,301],{},"user-callback"," entry from another origin coincides with the input. The user's tap was simply unlucky — but on a page where a tag fires on every scroll, it is unlucky for many users.",[15,304,305,55,308,311,312,315,316,318,319,322,323,325],{},[188,306,307],{},"4. The framework dispatcher masking the component.",[37,309,310],{},"invoker"," reads ",[37,313,314],{},"DIV#__next.onclick"," or similar for every interaction because React delegates all events at the root. Grouping by ",[37,317,310],{}," lumps everything together; grouping by ",[37,320,321],{},"sourceURL"," plus ",[37,324,214],{}," separates components.",[15,327,328],{},[48,329,55,332,55,335,55,338,55,340,55,343,55,345,55,353,55,360,55,367,55,372,55,376,55,381,55,385,55,389,55,393,55,397,55,401,55,405,55,409,55],{"viewBox":330,"width":51,"role":52,"ariaLabel":331,"style":54},"0 0 760 216","Bar chart of four script entries from one long frame, showing a promise continuation dominating a small listener.",[57,333],{"className":334,"x":61,"y":61,"width":51,"height":51,"fill":62},[60],[64,336,337],{},"Script time inside one failing filter interaction",[68,339,331],{},[57,341],{"x":90,"y":90,"width":91,"height":342,"rx":93,"fill":94,"stroke":86,"style":95},"214",[97,344,337],{"x":99,"y":100,"fill":86,"style":101},[160,346],{"x1":347,"y1":348,"x2":347,"y2":349,"stroke":140,"strokeWidth":165,"strokeDashArray":350,"style":352},"389.4","72.0","194.0",[351,351],"4","stroke-opacity:0.9",[97,354,359],{"x":355,"y":356,"fill":86,"style":357,"textAnchor":358},"172.5","92.0","font-size:13px","end","filters.js resolve-promise",[57,361],{"x":362,"y":113,"width":363,"height":364,"rx":365,"fill":139,"stroke":140,"style":366},"184.5","483.5","19","3","fill-opacity:0.7;stroke-opacity:0.9",[97,368,371],{"x":369,"y":356,"fill":86,"style":370},"674.0","font-size:12px;font-weight:600","118ms",[97,373,375],{"x":355,"y":374,"fill":86,"style":357,"textAnchor":358},"123.0","gtm.js user-callback",[57,377],{"x":362,"y":378,"width":379,"height":364,"rx":365,"fill":108,"stroke":108,"style":380},"109.0","188.5","fill-opacity:0.55;stroke-opacity:0.9",[97,382,384],{"x":383,"y":374,"fill":86,"style":370},"379.0","46ms",[97,386,388],{"x":355,"y":387,"fill":86,"style":357,"textAnchor":358},"154.0","react-dom commit",[57,390],{"x":362,"y":391,"width":392,"height":364,"rx":365,"fill":108,"stroke":108,"style":380},"140.0","155.7",[97,394,396],{"x":395,"y":387,"fill":86,"style":370},"346.2","38ms",[97,398,400],{"x":355,"y":399,"fill":86,"style":357,"textAnchor":358},"185.0","filters.js onClick",[57,402],{"x":362,"y":403,"width":404,"height":364,"rx":365,"fill":108,"stroke":108,"style":380},"171.0","24.6",[97,406,408],{"x":407,"y":399,"fill":86,"style":370},"215.1","6ms",[97,410,414],{"x":347,"y":411,"fill":412,"style":413,"textAnchor":115},"68.0","#8a6608","font-size:12px;font-weight:700","50ms per-task budget",[177,416,418],{"id":417},"step-by-step-resolution","Step-by-Step Resolution",[420,421,423],"h3",{"id":422},"_1-flatten-every-interactive-frame-into-script-rows","1. Flatten every interactive frame into script rows",[15,425,426],{},"Collect long frames with an observer registered early, then flatten them. A flat table is far easier to group than nested entries.",[428,429,434],"pre",{"className":430,"code":431,"language":432,"meta":433,"style":433},"language-javascript shiki shiki-themes github-light-high-contrast github-dark-high-contrast github-light-high-contrast","function scriptRows(frames) {\n  return frames\n    .filter((f) => f.firstUIEventTimestamp > 0 && f.duration > 50)\n    .flatMap((f) => f.scripts.map((s) => ({\n      frame: Math.round(f.startTime),\n      src: s.sourceURL || '(inline)',\n      fn: s.sourceFunctionName || '(anonymous)',\n      pos: s.sourceCharPosition,\n      type: s.invokerType,\n      invoker: s.invoker,\n      ms: s.duration,\n      layoutMs: s.forcedStyleAndLayoutDuration,\n    })));\n}\n\u002F\u002F trade-off: dropping frames without input hides the frame immediately before\n\u002F\u002F the tap, which is the one that inflates input delay. Include it when input\n\u002F\u002F delay, not processing, dominates your INP breakdown.\n","javascript","",[37,435,436,459,468,513,547,559,575,588,594,600,606,612,618,624,630,637,643],{"__ignoreMap":433},[437,438,440,444,448,452,456],"span",{"class":160,"line":439},1,[437,441,443],{"class":442},"sPARh","function",[437,445,447],{"class":446},"smZ65"," scriptRows",[437,449,451],{"class":450},"saISM","(",[437,453,455],{"class":454},"sQw3B","frames",[437,457,458],{"class":450},") {\n",[437,460,462,465],{"class":160,"line":461},2,[437,463,464],{"class":442},"  return",[437,466,467],{"class":450}," frames\n",[437,469,471,474,477,480,483,486,489,492,495,499,502,505,507,510],{"class":160,"line":470},3,[437,472,473],{"class":450},"    .",[437,475,476],{"class":446},"filter",[437,478,479],{"class":450},"((",[437,481,482],{"class":454},"f",[437,484,485],{"class":450},") ",[437,487,488],{"class":442},"=>",[437,490,491],{"class":450}," f.firstUIEventTimestamp ",[437,493,494],{"class":442},">",[437,496,498],{"class":497},"sPXB4"," 0",[437,500,501],{"class":442}," &&",[437,503,504],{"class":450}," f.duration ",[437,506,494],{"class":442},[437,508,509],{"class":497}," 50",[437,511,512],{"class":450},")\n",[437,514,516,518,521,523,525,527,529,532,535,537,540,542,544],{"class":160,"line":515},4,[437,517,473],{"class":450},[437,519,520],{"class":446},"flatMap",[437,522,479],{"class":450},[437,524,482],{"class":454},[437,526,485],{"class":450},[437,528,488],{"class":442},[437,530,531],{"class":450}," f.scripts.",[437,533,534],{"class":446},"map",[437,536,479],{"class":450},[437,538,539],{"class":454},"s",[437,541,485],{"class":450},[437,543,488],{"class":442},[437,545,546],{"class":450}," ({\n",[437,548,550,553,556],{"class":160,"line":549},5,[437,551,552],{"class":450},"      frame: Math.",[437,554,555],{"class":446},"round",[437,557,558],{"class":450},"(f.startTime),\n",[437,560,562,565,568,572],{"class":160,"line":561},6,[437,563,564],{"class":450},"      src: s.sourceURL ",[437,566,567],{"class":442},"||",[437,569,571],{"class":570},"sZ8jY"," '(inline)'",[437,573,574],{"class":450},",\n",[437,576,578,581,583,586],{"class":160,"line":577},7,[437,579,580],{"class":450},"      fn: s.sourceFunctionName ",[437,582,567],{"class":442},[437,584,585],{"class":570}," '(anonymous)'",[437,587,574],{"class":450},[437,589,591],{"class":160,"line":590},8,[437,592,593],{"class":450},"      pos: s.sourceCharPosition,\n",[437,595,597],{"class":160,"line":596},9,[437,598,599],{"class":450},"      type: s.invokerType,\n",[437,601,603],{"class":160,"line":602},10,[437,604,605],{"class":450},"      invoker: s.invoker,\n",[437,607,609],{"class":160,"line":608},11,[437,610,611],{"class":450},"      ms: s.duration,\n",[437,613,615],{"class":160,"line":614},12,[437,616,617],{"class":450},"      layoutMs: s.forcedStyleAndLayoutDuration,\n",[437,619,621],{"class":160,"line":620},13,[437,622,623],{"class":450},"    })));\n",[437,625,627],{"class":160,"line":626},14,[437,628,629],{"class":450},"}\n",[437,631,633],{"class":160,"line":632},15,[437,634,636],{"class":635},"sjfSM","\u002F\u002F trade-off: dropping frames without input hides the frame immediately before\n",[437,638,640],{"class":160,"line":639},16,[437,641,642],{"class":635},"\u002F\u002F the tap, which is the one that inflates input delay. Include it when input\n",[437,644,646],{"class":160,"line":645},17,[437,647,648],{"class":635},"\u002F\u002F delay, not processing, dominates your INP breakdown.\n",[15,650,651],{},"Expected outcome: a list of perhaps 20–60 rows per reproduction instead of a flame chart with thousands of nodes.",[420,653,655],{"id":654},"_2-group-by-source-and-function-rank-by-total-time","2. Group by source and function, rank by total time",[15,657,658],{},"Ranking by total time across frames, not by single worst entry, finds the code that costs the most interactions overall.",[428,660,662],{"className":430,"code":661,"language":432,"meta":433,"style":433},"function rank(rows) {\n  const groups = new Map();\n  for (const r of rows) {\n    const key = `${r.src}#${r.fn}@${r.pos}`;\n    const g = groups.get(key) || { key, type: r.type, total: 0, worst: 0, count: 0, layout: 0 };\n    g.total += r.ms; g.layout += r.layoutMs; g.count += 1;\n    g.worst = Math.max(g.worst, r.ms);\n    groups.set(key, g);\n  }\n  return [...groups.values()].sort((a, b) => b.total - a.total).slice(0, 10);\n}\nconsole.table(rank(scriptRows(frames)));\n\u002F\u002F trade-off: total time favours code that runs on every interaction. A rare\n\u002F\u002F but catastrophic handler (one 600ms checkout click) can rank low here — sort\n\u002F\u002F by `worst` as a second pass before deciding what to ship.\n",[37,663,664,678,698,718,765,808,831,848,859,864,923,927,948,953,958],{"__ignoreMap":433},[437,665,666,668,671,673,676],{"class":160,"line":439},[437,667,443],{"class":442},[437,669,670],{"class":446}," rank",[437,672,451],{"class":450},[437,674,675],{"class":454},"rows",[437,677,458],{"class":450},[437,679,680,683,686,689,692,695],{"class":160,"line":461},[437,681,682],{"class":442},"  const",[437,684,685],{"class":497}," groups",[437,687,688],{"class":442}," =",[437,690,691],{"class":442}," new",[437,693,694],{"class":446}," Map",[437,696,697],{"class":450},"();\n",[437,699,700,703,706,709,712,715],{"class":160,"line":470},[437,701,702],{"class":442},"  for",[437,704,705],{"class":450}," (",[437,707,708],{"class":442},"const",[437,710,711],{"class":497}," r",[437,713,714],{"class":442}," of",[437,716,717],{"class":450}," rows) {\n",[437,719,720,723,726,728,731,734,736,739,742,744,746,749,752,754,756,759,762],{"class":160,"line":515},[437,721,722],{"class":442},"    const",[437,724,725],{"class":497}," key",[437,727,688],{"class":442},[437,729,730],{"class":570}," `${",[437,732,733],{"class":450},"r",[437,735,210],{"class":570},[437,737,738],{"class":450},"src",[437,740,741],{"class":570},"}#${",[437,743,733],{"class":450},[437,745,210],{"class":570},[437,747,748],{"class":450},"fn",[437,750,751],{"class":570},"}@${",[437,753,733],{"class":450},[437,755,210],{"class":570},[437,757,758],{"class":450},"pos",[437,760,761],{"class":570},"}`",[437,763,764],{"class":450},";\n",[437,766,767,769,772,774,777,780,783,785,788,790,793,795,798,800,803,805],{"class":160,"line":549},[437,768,722],{"class":442},[437,770,771],{"class":497}," g",[437,773,688],{"class":442},[437,775,776],{"class":450}," groups.",[437,778,779],{"class":446},"get",[437,781,782],{"class":450},"(key) ",[437,784,567],{"class":442},[437,786,787],{"class":450}," { key, type: r.type, total: ",[437,789,61],{"class":497},[437,791,792],{"class":450},", worst: ",[437,794,61],{"class":497},[437,796,797],{"class":450},", count: ",[437,799,61],{"class":497},[437,801,802],{"class":450},", layout: ",[437,804,61],{"class":497},[437,806,807],{"class":450}," };\n",[437,809,810,813,816,819,821,824,826,829],{"class":160,"line":561},[437,811,812],{"class":450},"    g.total ",[437,814,815],{"class":442},"+=",[437,817,818],{"class":450}," r.ms; g.layout ",[437,820,815],{"class":442},[437,822,823],{"class":450}," r.layoutMs; g.count ",[437,825,815],{"class":442},[437,827,828],{"class":497}," 1",[437,830,764],{"class":450},[437,832,833,836,839,842,845],{"class":160,"line":577},[437,834,835],{"class":450},"    g.worst ",[437,837,838],{"class":442},"=",[437,840,841],{"class":450}," Math.",[437,843,844],{"class":446},"max",[437,846,847],{"class":450},"(g.worst, r.ms);\n",[437,849,850,853,856],{"class":160,"line":590},[437,851,852],{"class":450},"    groups.",[437,854,855],{"class":446},"set",[437,857,858],{"class":450},"(key, g);\n",[437,860,861],{"class":160,"line":596},[437,862,863],{"class":450},"  }\n",[437,865,866,868,871,874,877,880,883,886,888,890,893,896,898,900,903,906,909,912,914,916,918,920],{"class":160,"line":602},[437,867,464],{"class":442},[437,869,870],{"class":450}," [",[437,872,873],{"class":442},"...",[437,875,876],{"class":450},"groups.",[437,878,879],{"class":446},"values",[437,881,882],{"class":450},"()].",[437,884,885],{"class":446},"sort",[437,887,479],{"class":450},[437,889,19],{"class":454},[437,891,892],{"class":450},", ",[437,894,895],{"class":454},"b",[437,897,485],{"class":450},[437,899,488],{"class":442},[437,901,902],{"class":450}," b.total ",[437,904,905],{"class":442},"-",[437,907,908],{"class":450}," a.total).",[437,910,911],{"class":446},"slice",[437,913,451],{"class":450},[437,915,61],{"class":497},[437,917,892],{"class":450},[437,919,93],{"class":497},[437,921,922],{"class":450},");\n",[437,924,925],{"class":160,"line":608},[437,926,629],{"class":450},[437,928,929,932,935,937,940,942,945],{"class":160,"line":614},[437,930,931],{"class":450},"console.",[437,933,934],{"class":446},"table",[437,936,451],{"class":450},[437,938,939],{"class":446},"rank",[437,941,451],{"class":450},[437,943,944],{"class":446},"scriptRows",[437,946,947],{"class":450},"(frames)));\n",[437,949,950],{"class":160,"line":620},[437,951,952],{"class":635},"\u002F\u002F trade-off: total time favours code that runs on every interaction. A rare\n",[437,954,955],{"class":160,"line":626},[437,956,957],{"class":635},"\u002F\u002F but catastrophic handler (one 600ms checkout click) can rank low here — sort\n",[437,959,960],{"class":160,"line":632},[437,961,962],{"class":635},"\u002F\u002F by `worst` as a second pass before deciding what to ship.\n",[15,964,965],{},"Expected outcome: the top two or three keys usually account for over half of all long-frame script time.",[420,967,969],{"id":968},"_3-resolve-positions-through-source-maps","3. Resolve positions through source maps",[15,971,972,974,975,978],{},[37,973,214],{}," is a character offset into the ",[30,976,977],{},"served"," file. Convert it to a line and column, then run it through the source map to reach the original component.",[428,980,982],{"className":430,"code":981,"language":432,"meta":433,"style":433},"\u002F\u002F node resolve-position.mjs dist\u002Fassets\u002Ffilters-3f9a.js 48211\nimport { readFileSync } from 'node:fs';\nimport { SourceMapConsumer } from 'source-map';\nconst [file, offset] = process.argv.slice(2);\nconst code = readFileSync(file, 'utf8').slice(0, Number(offset));\nconst line = code.split('\\n').length;\nconst column = code.length - code.lastIndexOf('\\n') - 1;\nconst map = JSON.parse(readFileSync(`${file}.map`, 'utf8'));\nawait SourceMapConsumer.with(map, null, (c) =>\n  console.log(c.originalPositionFor({ line, column })));\n\u002F\u002F trade-off: this needs the exact build's .map file. Keep source maps as private\n\u002F\u002F build artefacts per release; resolving against a later build gives a\n\u002F\u002F confidently wrong line.\n",[37,983,984,989,1005,1019,1049,1084,1116,1153,1192,1219,1236,1241,1246],{"__ignoreMap":433},[437,985,986],{"class":160,"line":439},[437,987,988],{"class":635},"\u002F\u002F node resolve-position.mjs dist\u002Fassets\u002Ffilters-3f9a.js 48211\n",[437,990,991,994,997,1000,1003],{"class":160,"line":461},[437,992,993],{"class":442},"import",[437,995,996],{"class":450}," { readFileSync } ",[437,998,999],{"class":442},"from",[437,1001,1002],{"class":570}," 'node:fs'",[437,1004,764],{"class":450},[437,1006,1007,1009,1012,1014,1017],{"class":160,"line":470},[437,1008,993],{"class":442},[437,1010,1011],{"class":450}," { SourceMapConsumer } ",[437,1013,999],{"class":442},[437,1015,1016],{"class":570}," 'source-map'",[437,1018,764],{"class":450},[437,1020,1021,1023,1025,1027,1029,1032,1035,1037,1040,1042,1044,1047],{"class":160,"line":515},[437,1022,708],{"class":442},[437,1024,870],{"class":450},[437,1026,158],{"class":497},[437,1028,892],{"class":450},[437,1030,1031],{"class":497},"offset",[437,1033,1034],{"class":450},"] ",[437,1036,838],{"class":442},[437,1038,1039],{"class":450}," process.argv.",[437,1041,911],{"class":446},[437,1043,451],{"class":450},[437,1045,1046],{"class":497},"2",[437,1048,922],{"class":450},[437,1050,1051,1053,1056,1058,1061,1064,1067,1070,1072,1074,1076,1078,1081],{"class":160,"line":549},[437,1052,708],{"class":442},[437,1054,1055],{"class":497}," code",[437,1057,688],{"class":442},[437,1059,1060],{"class":446}," readFileSync",[437,1062,1063],{"class":450},"(file, ",[437,1065,1066],{"class":570},"'utf8'",[437,1068,1069],{"class":450},").",[437,1071,911],{"class":446},[437,1073,451],{"class":450},[437,1075,61],{"class":497},[437,1077,892],{"class":450},[437,1079,1080],{"class":446},"Number",[437,1082,1083],{"class":450},"(offset));\n",[437,1085,1086,1088,1091,1093,1096,1099,1101,1104,1107,1109,1111,1114],{"class":160,"line":561},[437,1087,708],{"class":442},[437,1089,1090],{"class":497}," line",[437,1092,688],{"class":442},[437,1094,1095],{"class":450}," code.",[437,1097,1098],{"class":446},"split",[437,1100,451],{"class":450},[437,1102,1103],{"class":570},"'",[437,1105,1106],{"class":442},"\\n",[437,1108,1103],{"class":570},[437,1110,1069],{"class":450},[437,1112,1113],{"class":497},"length",[437,1115,764],{"class":450},[437,1117,1118,1120,1123,1125,1127,1129,1132,1134,1137,1139,1141,1143,1145,1147,1149,1151],{"class":160,"line":577},[437,1119,708],{"class":442},[437,1121,1122],{"class":497}," column",[437,1124,688],{"class":442},[437,1126,1095],{"class":450},[437,1128,1113],{"class":497},[437,1130,1131],{"class":442}," -",[437,1133,1095],{"class":450},[437,1135,1136],{"class":446},"lastIndexOf",[437,1138,451],{"class":450},[437,1140,1103],{"class":570},[437,1142,1106],{"class":442},[437,1144,1103],{"class":570},[437,1146,485],{"class":450},[437,1148,905],{"class":442},[437,1150,828],{"class":497},[437,1152,764],{"class":450},[437,1154,1155,1157,1160,1162,1165,1167,1170,1172,1175,1177,1180,1182,1185,1187,1189],{"class":160,"line":590},[437,1156,708],{"class":442},[437,1158,1159],{"class":497}," map",[437,1161,688],{"class":442},[437,1163,1164],{"class":497}," JSON",[437,1166,210],{"class":450},[437,1168,1169],{"class":446},"parse",[437,1171,451],{"class":450},[437,1173,1174],{"class":446},"readFileSync",[437,1176,451],{"class":450},[437,1178,1179],{"class":570},"`${",[437,1181,158],{"class":450},[437,1183,1184],{"class":570},"}.map`",[437,1186,892],{"class":450},[437,1188,1066],{"class":570},[437,1190,1191],{"class":450},"));\n",[437,1193,1194,1196,1199,1202,1205,1208,1211,1214,1216],{"class":160,"line":596},[437,1195,251],{"class":442},[437,1197,1198],{"class":450}," SourceMapConsumer.",[437,1200,1201],{"class":446},"with",[437,1203,1204],{"class":450},"(map, ",[437,1206,1207],{"class":497},"null",[437,1209,1210],{"class":450},", (",[437,1212,1213],{"class":454},"c",[437,1215,485],{"class":450},[437,1217,1218],{"class":442},"=>\n",[437,1220,1221,1224,1227,1230,1233],{"class":160,"line":602},[437,1222,1223],{"class":450},"  console.",[437,1225,1226],{"class":446},"log",[437,1228,1229],{"class":450},"(c.",[437,1231,1232],{"class":446},"originalPositionFor",[437,1234,1235],{"class":450},"({ line, column })));\n",[437,1237,1238],{"class":160,"line":608},[437,1239,1240],{"class":635},"\u002F\u002F trade-off: this needs the exact build's .map file. Keep source maps as private\n",[437,1242,1243],{"class":160,"line":614},[437,1244,1245],{"class":635},"\u002F\u002F build artefacts per release; resolving against a later build gives a\n",[437,1247,1248],{"class":160,"line":620},[437,1249,1250],{"class":635},"\u002F\u002F confidently wrong line.\n",[15,1252,1253],{},"Expected outcome: each top-ranked row gains a component name and an owning team, which is what turns a metric into a ticket.",[420,1255,1257],{"id":1256},"_4-apply-the-fix-the-invoker-type-implies","4. Apply the fix the invoker type implies",[182,1259,1260,1272,1281,1294],{},[185,1261,1262,1264,1265,1267,1268,210],{},[37,1263,243],{}," over ",[37,1266,125],{}," → split with an await-yield loop; see ",[19,1269,1271],{"href":1270},"\u002Fcore-web-vitals-measurement\u002Foptimizing-inp-with-scheduler-yield\u002Fbreaking-up-long-tasks-in-react-event-handlers\u002F","breaking up long tasks in React event handlers",[185,1273,1274,1276,1277,1280],{},[37,1275,247],{}," dominating → move the expensive continuation behind ",[37,1278,1279],{},"scheduler.yield()"," or into a worker.",[185,1282,1283,1284,298,1286,1288,1289,1293],{},"Third-party ",[37,1285,255],{},[37,1287,301],{}," → defer the vendor; ",[19,1290,1292],{"href":1291},"\u002Fcore-web-vitals-measurement\u002Fprofiling-event-handlers-for-inp\u002Freducing-input-delay-from-third-party-tags\u002F","reducing input delay from third-party tags"," shows the patterns.",[185,1295,1296,1299,1300,1303],{},[37,1297,1298],{},"layoutMs"," close to ",[37,1301,1302],{},"ms"," → batch reads before writes.",[15,1305,1306],{},"Expected outcome: fixing the top-ranked group typically removes 80–150ms from the slowest interaction on mid-tier mobile.",[15,1308,1309],{},[48,1310,55,1313,55,1316,55,1319,55,1321,55,1324,55,1326,55,1331,55,1337,55,1341,55,1345,55,1348,55,1352,55,1356,55,1359,55,1361,55,1364,55,1366,55,1369,55,1372,55,1375,55,1377,55,1380,55,1382,55,1385,55,1387,55,1390,55,1392,55,1395,55,1397,55,1400,55,1403,55,1406,55,1408,55,1411,55,1413,55],{"viewBox":1311,"width":51,"role":52,"ariaLabel":1312,"style":54},"0 0 760 228","Mapping of each LoAF invoker type to the first fix to try and how much INP it typically recovers.",[57,1314],{"className":1315,"x":61,"y":61,"width":51,"height":51,"fill":62},[60],[64,1317,1318],{},"Invoker type to first fix",[68,1320,1312],{},[57,1322],{"x":90,"y":90,"width":91,"height":1323,"rx":93,"fill":94,"stroke":86,"style":95},"226",[97,1325,1318],{"x":99,"y":100,"fill":86,"style":101},[57,1327],{"x":99,"y":104,"width":1328,"height":1329,"rx":61,"fill":86,"stroke":86,"style":1330},"114.0","30.0","fill-opacity:0.06;stroke-opacity:0.4",[97,1332,240],{"x":1333,"y":1334,"fill":86,"style":1335,"textAnchor":1336},"38.0","75.5","font-size:12.5px;font-weight:700","start",[57,1338],{"x":1339,"y":104,"width":1340,"height":1329,"rx":61,"fill":86,"stroke":86,"style":1330},"142.0","295.0",[97,1342,1344],{"x":1343,"y":1334,"fill":86,"style":1335,"textAnchor":115},"289.5","First fix",[57,1346],{"x":1347,"y":104,"width":1340,"height":1329,"rx":61,"fill":86,"stroke":86,"style":1330},"437.0",[97,1349,1351],{"x":1350,"y":1334,"fill":86,"style":1335,"textAnchor":115},"584.5","Typical gain",[57,1353],{"x":99,"y":1354,"width":1328,"height":1329,"rx":61,"fill":94,"stroke":86,"style":1355},"86.0","stroke-opacity:0.35",[97,1357,243],{"x":1333,"y":1358,"fill":86,"style":1335,"textAnchor":1336},"105.5",[57,1360],{"x":1339,"y":1354,"width":1340,"height":1329,"rx":61,"fill":94,"stroke":86,"style":1355},[97,1362,1363],{"x":1343,"y":1358,"fill":86,"style":120,"textAnchor":115},"Yield inside the handler",[57,1365],{"x":1347,"y":1354,"width":1340,"height":1329,"rx":61,"fill":108,"stroke":108,"style":109},[97,1367,1368],{"x":1350,"y":1358,"fill":86,"style":120,"textAnchor":115},"60-150ms",[57,1370],{"x":99,"y":1371,"width":1328,"height":1329,"rx":61,"fill":94,"stroke":86,"style":1355},"116.0",[97,1373,247],{"x":1333,"y":1374,"fill":86,"style":1335,"textAnchor":1336},"135.5",[57,1376],{"x":1339,"y":1371,"width":1340,"height":1329,"rx":61,"fill":94,"stroke":86,"style":1355},[97,1378,1379],{"x":1343,"y":1374,"fill":86,"style":120,"textAnchor":115},"Yield after the await",[57,1381],{"x":1347,"y":1371,"width":1340,"height":1329,"rx":61,"fill":108,"stroke":108,"style":109},[97,1383,1384],{"x":1350,"y":1374,"fill":86,"style":120,"textAnchor":115},"40-120ms",[57,1386],{"x":99,"y":105,"width":1328,"height":1329,"rx":61,"fill":94,"stroke":86,"style":1355},[97,1388,255],{"x":1333,"y":1389,"fill":86,"style":1335,"textAnchor":1336},"165.5",[57,1391],{"x":1339,"y":105,"width":1340,"height":1329,"rx":61,"fill":94,"stroke":86,"style":1355},[97,1393,1394],{"x":1343,"y":1389,"fill":86,"style":120,"textAnchor":115},"Defer vendor to idle",[57,1396],{"x":1347,"y":105,"width":1340,"height":1329,"rx":61,"fill":86,"stroke":86,"style":1330},[97,1398,1399],{"x":1350,"y":1389,"fill":86,"style":120,"textAnchor":115},"varies by tag",[57,1401],{"x":99,"y":1402,"width":1328,"height":1329,"rx":61,"fill":94,"stroke":86,"style":1355},"176.0",[97,1404,301],{"x":1333,"y":1405,"fill":86,"style":1335,"textAnchor":1336},"195.5",[57,1407],{"x":1339,"y":1402,"width":1340,"height":1329,"rx":61,"fill":94,"stroke":86,"style":1355},[97,1409,1410],{"x":1343,"y":1405,"fill":86,"style":120,"textAnchor":115},"Throttle the timer or rAF",[57,1412],{"x":1347,"y":1402,"width":1340,"height":1329,"rx":61,"fill":86,"stroke":86,"style":1330},[97,1414,1415],{"x":1350,"y":1405,"fill":86,"style":120,"textAnchor":115},"20-80ms",[177,1417,1419],{"id":1418},"verification","Verification",[15,1421,1422,1423,1426],{},"Re-run the same scripted interaction and regenerate the ranked table. The fixed key should drop out of the top three, and the interaction's longest frame should fall below ",[37,1424,1425],{},"100ms"," in a 4x-throttled lab run. Then ship and compare field data: INP processing duration for the affected interaction target should fall, and the share of INP beacons naming the fixed function should shrink to near zero within a release cycle.",[428,1428,1430],{"className":430,"code":1429,"language":432,"meta":433,"style":433},"\u002F\u002F Compare before\u002Fafter script totals for one key across two lab runs.\nconst before = 182, after = rank(scriptRows(frames)).find((g) => g.key.includes('applyFilters'))?.total ?? 0;\nconsole.log(`applyFilters: ${before}ms -> ${Math.round(after)}ms`);\n\u002F\u002F trade-off: a single lab run is noisy (±15%). Run five and compare medians\n\u002F\u002F before declaring victory or failure.\n",[37,1431,1432,1437,1498,1534,1539],{"__ignoreMap":433},[437,1433,1434],{"class":160,"line":439},[437,1435,1436],{"class":635},"\u002F\u002F Compare before\u002Fafter script totals for one key across two lab runs.\n",[437,1438,1439,1441,1444,1446,1449,1451,1454,1456,1458,1460,1462,1465,1468,1470,1473,1475,1477,1480,1483,1485,1488,1491,1494,1496],{"class":160,"line":461},[437,1440,708],{"class":442},[437,1442,1443],{"class":497}," before",[437,1445,688],{"class":442},[437,1447,1448],{"class":497}," 182",[437,1450,892],{"class":450},[437,1452,1453],{"class":497},"after",[437,1455,688],{"class":442},[437,1457,670],{"class":446},[437,1459,451],{"class":450},[437,1461,944],{"class":446},[437,1463,1464],{"class":450},"(frames)).",[437,1466,1467],{"class":446},"find",[437,1469,479],{"class":450},[437,1471,1472],{"class":454},"g",[437,1474,485],{"class":450},[437,1476,488],{"class":442},[437,1478,1479],{"class":450}," g.key.",[437,1481,1482],{"class":446},"includes",[437,1484,451],{"class":450},[437,1486,1487],{"class":570},"'applyFilters'",[437,1489,1490],{"class":450},"))?.total ",[437,1492,1493],{"class":442},"??",[437,1495,498],{"class":497},[437,1497,764],{"class":450},[437,1499,1500,1502,1504,1506,1509,1512,1515,1518,1520,1522,1524,1526,1529,1532],{"class":160,"line":470},[437,1501,931],{"class":450},[437,1503,1226],{"class":446},[437,1505,451],{"class":450},[437,1507,1508],{"class":570},"`applyFilters: ${",[437,1510,1511],{"class":450},"before",[437,1513,1514],{"class":570},"}ms -> ${",[437,1516,1517],{"class":450},"Math",[437,1519,210],{"class":570},[437,1521,555],{"class":446},[437,1523,451],{"class":570},[437,1525,1453],{"class":450},[437,1527,1528],{"class":570},")",[437,1530,1531],{"class":570},"}ms`",[437,1533,922],{"class":450},[437,1535,1536],{"class":160,"line":515},[437,1537,1538],{"class":635},"\u002F\u002F trade-off: a single lab run is noisy (±15%). Run five and compare medians\n",[437,1540,1541],{"class":160,"line":549},[437,1542,1543],{"class":635},"\u002F\u002F before declaring victory or failure.\n",[177,1545,1547],{"id":1546},"reading-attribution-on-framework-heavy-pages","Reading Attribution on Framework-Heavy Pages",[15,1549,1550],{},"Single-page applications blur the line between \"a script\" and \"a component\", and the raw rows need interpretation before they become tickets.",[15,1552,1553,1556,1557,1559,1560,1562,1563,1566,1567,1569,1570,1572,1573,1575],{},[188,1554,1555],{},"React and Preact."," Event delegation means ",[37,1558,310],{}," is the root container for almost every interaction, and the expensive ",[37,1561,243],{}," entry is React's dispatcher in ",[37,1564,1565],{},"react-dom",". The useful signal is the next entry — commonly a ",[37,1568,247],{}," or a second ",[37,1571,243],{}," from your own chunk — and ",[37,1574,214],{}," within the dispatcher is meaningless. Combine LoAF with the React Profiler's commit timings to find the component; LoAF tells you whether the commit or the handler is the larger cost.",[15,1577,1578,1581,1582,1584,1585,1588,1589,1591],{},[188,1579,1580],{},"Vue and Nuxt."," Vue binds listeners on the actual elements, so ",[37,1583,310],{}," is usually informative (",[37,1586,1587],{},"BUTTON.apply-filter.onclick","). The cost frequently appears as a ",[37,1590,247],{}," entry from the scheduler flushing watchers after the handler — the reactive work, not the handler body.",[15,1593,1594,1597,1598,1600,1601,1604],{},[188,1595,1596],{},"Angular with zone.js."," Patched APIs wrap every callback, so ",[37,1599,321],{}," points at ",[37,1602,1603],{},"zone.js"," or the polyfills chunk for many entries. Resolve with function names rather than URLs, or test against a zoneless build where attribution points at application code directly.",[15,1606,1607,1610,1611,892,1614,1617],{},[188,1608,1609],{},"Micro-frontends."," Several independently built bundles on one page each produce their own chunk URLs. Group by a path prefix per micro-frontend (",[37,1612,1613],{},"\u002Fmf\u002Fcheckout\u002F",[37,1615,1616],{},"\u002Fmf\u002Fsearch\u002F",") before ranking; it maps directly to team ownership and avoids arguing over shared vendor chunks.",[15,1619,1620],{},"In every case, store the build ID alongside the rows so a release that changes chunk names does not split one function's history into two keys.",[177,1622,1624],{"id":1623},"faq","FAQ",[1626,1627,1630,1634],"details",{"className":1628},[1629],"faq-item",[1631,1632,1633],"summary",{},"Why are some script entries missing from the frame?",[15,1635,1636],{},"The specification only reports scripts above a minimum duration (a few milliseconds), and only those that are attributable to a script execution — browser-internal work and rendering are reported as frame phases instead. A frame built from very many tiny callbacks may therefore show a long duration with few script rows; that shape usually means a reactive store fan-out rather than one slow function.",[1626,1638,1640,1643],{"className":1639},[1629],[1631,1641,1642],{},"Can I get function names for third-party scripts?",[15,1644,1645,1646,1649,1650,1653,1654,1656,1657,1660],{},"Only if the script is loaded with ",[37,1647,1648],{},"crossorigin=\"anonymous\""," and the vendor responds with an ",[37,1651,1652],{},"Access-Control-Allow-Origin"," header. Without both, you still get the ",[37,1655,321],{},", which is normally enough to know which vendor to defer. Adding ",[37,1658,1659],{},"crossorigin"," to a script whose server does not send CORS headers will make the load fail, so verify the header first.",[177,1662,1664],{"id":1663},"related","Related",[182,1666,1667,1674,1681],{},[185,1668,1669,1673],{},[19,1670,1672],{"href":1671},"\u002Fcore-web-vitals-measurement\u002Flong-animation-frames-api\u002Floaf-vs-long-tasks-api\u002F","LoAF vs the Long Tasks API"," — why the old observer could never name a script.",[185,1675,1676,1680],{},[19,1677,1679],{"href":1678},"\u002Fcore-web-vitals-measurement\u002Flong-animation-frames-api\u002Fsending-loaf-data-in-rum-beacons\u002F","Sending LoAF data in RUM beacons"," — getting these rows from real users instead of a lab trace.",[185,1682,1683,1687],{},[19,1684,1686],{"href":1685},"\u002Fcore-web-vitals-measurement\u002Fprofiling-event-handlers-for-inp\u002Ffinding-the-slowest-interaction-in-the-performance-panel\u002F","Finding the slowest interaction in the Performance panel"," — choosing which interaction to reproduce in the first place.",[1689,1690,1692],"script",{"type":1691},"application\u002Fld+json","\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"HowTo\",\n  \"name\": \"How to Find the Script Behind a Slow Interaction with LoAF Attribution\",\n  \"description\": \"Turn the scripts array of a long animation frame into a ranked, source-mapped list of the code that is costing you INP.\",\n  \"step\": [\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 1,\n      \"name\": \"Flatten every interactive frame into script rows\",\n      \"text\": \"Collect long frames with an observer registered early, then flatten them.\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 2,\n      \"name\": \"Group by source and function, rank by total time\",\n      \"text\": \"Ranking by total time across frames, not by single worst entry, finds the code that costs the most interactions overall.\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 3,\n      \"name\": \"Resolve positions through source maps\",\n      \"text\": \"sourceCharPosition is a character offset into the served file.\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 4,\n      \"name\": \"Apply the fix the invoker type implies\",\n      \"text\": \"Expected outcome: fixing the top-ranked group typically removes 80–150ms from the slowest interaction on mid-tier mobile.\"\n    }\n  ]\n}\n",[1689,1694,1695],{"type":1691},"\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"TechArticle\",\n  \"headline\": \"How to Find the Script Behind a Slow Interaction with LoAF Attribution\",\n  \"description\": \"Turn the scripts array of a long animation frame into a ranked, source-mapped list of the code that is costing you INP.\",\n  \"datePublished\": \"2026-10-06\",\n  \"dateModified\": \"2026-10-06\",\n  \"author\": {\n    \"@type\": \"Organization\",\n    \"name\": \"frontend-performance.com\"\n  },\n  \"publisher\": {\n    \"@type\": \"Organization\",\n    \"name\": \"frontend-performance.com\"\n  },\n  \"mainEntityOfPage\": {\n    \"@type\": \"WebPage\",\n    \"@id\": \"https:\u002F\u002Ffrontend-performance.com\u002Fcore-web-vitals-measurement\u002Flong-animation-frames-api\u002Ffinding-slow-scripts-with-loaf-script-attribution\u002F\"\n  }\n}\n",[1689,1697,1698],{"type":1691},"\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"BreadcrumbList\",\n  \"itemListElement\": [\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 1,\n      \"name\": \"Home\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002F\"\n    },\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 2,\n      \"name\": \"Core Web Vitals & Measurement\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fcore-web-vitals-measurement\u002F\"\n    },\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 3,\n      \"name\": \"Long Animation Frames (LoAF) API\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fcore-web-vitals-measurement\u002Flong-animation-frames-api\u002F\"\n    },\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 4,\n      \"name\": \"Finding Slow Scripts with LoAF Script Attribution\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fcore-web-vitals-measurement\u002Flong-animation-frames-api\u002Ffinding-slow-scripts-with-loaf-script-attribution\u002F\"\n    }\n  ]\n}\n",[1700,1701,1702],"style",{},"html pre.shiki code .sPARh, html code.shiki .sPARh{--shiki-default:#A0111F;--shiki-dark:#FF9492;--shiki-light:#A0111F}html pre.shiki code .smZ65, html code.shiki .smZ65{--shiki-default:#622CBC;--shiki-dark:#DBB7FF;--shiki-light:#622CBC}html pre.shiki code .saISM, html code.shiki .saISM{--shiki-default:#0E1116;--shiki-dark:#F0F3F6;--shiki-light:#0E1116}html pre.shiki code .sQw3B, html code.shiki .sQw3B{--shiki-default:#702C00;--shiki-dark:#FFB757;--shiki-light:#702C00}html pre.shiki code .sPXB4, html code.shiki .sPXB4{--shiki-default:#023B95;--shiki-dark:#91CBFF;--shiki-light:#023B95}html pre.shiki code .sZ8jY, html code.shiki .sZ8jY{--shiki-default:#032563;--shiki-dark:#ADDCFF;--shiki-light:#032563}html pre.shiki code .sjfSM, html code.shiki .sjfSM{--shiki-default:#66707B;--shiki-dark:#BDC4CC;--shiki-light:#66707B}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}",{"title":433,"searchDepth":461,"depth":461,"links":1704},[1705,1706,1707,1713,1714,1715,1716],{"id":179,"depth":461,"text":180},{"id":259,"depth":461,"text":260},{"id":417,"depth":461,"text":418,"children":1708},[1709,1710,1711,1712],{"id":422,"depth":470,"text":423},{"id":654,"depth":470,"text":655},{"id":968,"depth":470,"text":969},{"id":1256,"depth":470,"text":1257},{"id":1418,"depth":461,"text":1419},{"id":1546,"depth":461,"text":1547},{"id":1623,"depth":461,"text":1624},{"id":1663,"depth":461,"text":1664},"Turn the scripts array of a long animation frame into a ranked, source-mapped list of the code that is costing you INP.","md",{"slug":1720,"type":1721,"breadcrumb":1722,"datePublished":1731,"dateModified":1731},"finding-slow-scripts-with-loaf-script-attribution","article",[1723,1726,1727,1729],{"name":1724,"url":1725},"Home","\u002F",{"name":27,"url":26},{"name":1728,"url":21},"Long Animation Frames (LoAF) API",{"name":5,"url":1730},"\u002Fcore-web-vitals-measurement\u002Flong-animation-frames-api\u002Ffinding-slow-scripts-with-loaf-script-attribution\u002F","2026-10-06",true,"\u002Fcore-web-vitals-measurement\u002Flong-animation-frames-api\u002Ffinding-slow-scripts-with-loaf-script-attribution",{"title":5,"description":1735},"Rank the scripts behind slow interactions using LoAF sourceURL, function name and invoker fields, then map them to source and fix the worst one first.","core-web-vitals-measurement\u002Flong-animation-frames-api\u002Ffinding-slow-scripts-with-loaf-script-attribution\u002Findex","a86_lWOHvxXAio7EnZuZcumuQniaKQ9mZNe_VJtCVJo",[1739,1743],{"title":1740,"path":1741,"stem":1742},"Diagnosing Presentation Delay with LoAF","\u002Fcore-web-vitals-measurement\u002Flong-animation-frames-api\u002Fdiagnosing-presentation-delay-with-loaf","core-web-vitals-measurement\u002Flong-animation-frames-api\u002Fdiagnosing-presentation-delay-with-loaf\u002Findex",{"title":1672,"path":1744,"stem":1745},"\u002Fcore-web-vitals-measurement\u002Flong-animation-frames-api\u002Floaf-vs-long-tasks-api","core-web-vitals-measurement\u002Flong-animation-frames-api\u002Floaf-vs-long-tasks-api\u002Findex",1791308078846]