WebKit Bugzilla
New
Browse
Search+
Log In
×
Sign in with GitHub
or
Remember my login
Create Account
·
Forgot Password
Forgotten password account recovery
RESOLVED FIXED
322283
[scroll-animations] Style resolution with scroll-timeline name blocks main thread for multiple seconds
https://bugs.webkit.org/show_bug.cgi?id=322283
Summary
[scroll-animations] Style resolution with scroll-timeline name blocks main th...
Jan Nicklas
Reported
2026-08-21 09:37:20 PDT
Created
attachment 481126
[details]
self-contained reduction no network access required This about a performance bug in Webkit when using the new css feature scroll-timeline When N elements each declare a named scroll timeline using the SAME name and N animations consume that name the cost of inserting them grows roughly as N^2.8. Safari Technology Preview 249, macOS, one tile = one declaration and one animation: 300 tiles 420 ms shared 24 ms unique 1000 tiles 14538 ms shared Chrome is flat at 2-11ms in both modes at every size, so the cost does not appear to be inherent to the feature. It also accumulates. Rebuilding the same 300 tiles six times in one document costs 430, 729, 994, 1301, 1591, 1862 ms and does not level off Steps to Reproduce: 1. Open the attached index.html (attached) It defaults to ?tiles=300&mode=shared 2. The number printed at the top of the page is duration the render took for the innerHTML change plus a forced layout (measured with performance.now()) 3. Click "test 1000 scroll-timelines" to run it with 1000 tiles and see the page freeze 4. Click "test 1000 different scroll-timeline names" 4. See that now it no longer freezes The whole reduction is three elements per tile, repeated N times: <div style="timeline-scope:--t"> <div class="scroller" style="scroll-timeline:--t inline"> <div class="content"></div> </div> <span class="dot" style="animation-timeline:--t"></span> </div> In mode=shared every tile uses the name "--t". In mode=unique it uses a different name per tile: "--t<i>" Actual Results: Safari Technology Preview 249 (WebKit 21626.1.1), one fresh page load per measurement (about N^2.8): tiles shared name unique name ratio 50 5 ms 2 ms 2.5x 100 20 ms 5 ms 4.0x 200 134 ms 13 ms 10.3x 300 420 ms 24 ms 17.5x 400 976 ms 40 ms 24.4x 600 3185 ms 84 ms 37.9x 1000 14538 ms 204 ms 71.3x Also tried Safari 26.6 (21624.4.5.11.5) which blocked the main thread for 18332 ms (1000 tiles) Expected Results: Resolving a timeline name should not get more expensive for each additional element that declares the same name. The two columns above should be comparable, as they are in Chrome. Build Date & Hardware: Safari 26.6 (21624.4.5.11.5) Safari Technology Preview 249 (21626.1.1) macOS 26.6.1 (25G76) MacBook Pro, Apple M1 Max, 64 GB Additional Builds and Platforms: Chrome 141 (Blink), same files, same machine: 2-11ms across 50-1000 tiles No measurable difference between shared and unique names. Real iOS devices, via BrowserStack, one fresh navigation per measurement: iPhone 17 Pro, iOS 26, Safari 26.5 300 tiles: 233 ms shared 43 ms unique 5.4x 600 tiles: 1576 ms shared 88 ms unique 17.9x iPhone 14, iOS 26, Safari 26.2 300 tiles: 387 ms shared 32 ms unique 12.1x 400 tiles: 846 ms shared 40 ms unique 21.1x iPhone 16 Pro, iOS 18, Safari 18.4 ‼️ no timeline support 300 tiles: 18 ms shared 18 ms unique 1.0x Additional Information: Registrations appear not to be released when the declaring elements are replaced. Rebuilding the SAME 300 tiles six times inside one document: pass 1 2 3 4 5 6 shared 430 729 994 1301 1591 1862 ms unique 23 36 37 39 40 40 ms Same shape on iPhone 17 Pro / iOS 26: 210, 310, 453, 611, 779, 923 ms shared against 41, 29, 20, 18, 19, 19 unique. This is the part that hurt us in production. A product list re-renders on filter, sort and pagination, so each interaction pays for every render before To see it with the attached file, open ?tiles=300&mode=shared and re-run the build in the console a few times: const app = document.getElementById("app"); const html = app.innerHTML; for (let i = 0; i < 6; i++) { const t = performance.now(); app.innerHTML = html; document.body.offsetHeight; console.log(Math.round(performance.now() - t), "ms", document.getAnimations().length, "animations"); } I have not built or profiled WebKit, so this is a guess. I believe the cause could be in StyleOriginatedTimelinesController and registerNamedScrollTimeline() as it calls updateCSSAnimationsAssociatedWithNamedTimeline(name) which re-resolves every animation already bound to that name
Attachments
self-contained reduction no network access required
(2.15 KB, text/html)
2026-08-21 09:37 PDT
,
Jan Nicklas
no flags
Details
Screenshot of the Reproduction
(271.60 KB, image/png)
2026-08-21 11:02 PDT
,
Jan Nicklas
no flags
Details
View All
Add attachment
proposed patch, testcase, etc.
Jan Nicklas
Comment 1
2026-08-21 11:02:56 PDT
Created
attachment 481127
[details]
Screenshot of the Reproduction
Radar WebKit Bug Importer
Comment 2
2026-08-28 09:38:12 PDT
<
rdar://problem/186090970
>
Antoine Quint
Comment 3
2026-09-02 08:20:28 PDT
This is pretty egregious and reproduces on macOS. On my MacBook Pro (Apple M4 Max, 128 GB) with a
320320@main
build, I get the following: 100: 16ms 300: 360ms 600: 2989ms 1000: 14874ms On Chrome, I get: 100: 2ms 300: 5ms 600: 6ms 1000: 15ms On Firefox Nightly, I get: 100: 23ms 300: 1211ms 600: 16638ms 1000: 139063ms (that's over 2 minutes) So Chrome performs much, much better than WebKit, and Firefox performs much worse.
Antoine Quint
Comment 4
2026-09-02 09:29:44 PDT
We're spending almost all of our time under `StyleOriginatedTimelinesController::attachAnimation()` here: about 50% in the function itself, ~30% in `originatingStyleableIncludingTimelineScope()` and 12% in `originatingElement()`.
Antoine Quint
Comment 5
2026-09-09 07:47:26 PDT
This was made worse by
320040@main
and then increasingly more so by
320183@main
.
Antoine Quint
Comment 6
2026-09-10 07:54:10 PDT
Pull request:
https://github.com/WebKit/WebKit/pull/73672
EWS
Comment 7
2026-09-10 22:51:26 PDT
Committed
320902@main
(3ef0f816b871): <
https://commits.webkit.org/320902@main
> Reviewed commits have been landed. Closing PR #73672 and removing active labels.
Note
You need to
log in
before you can comment on or make changes to this bug.
Top of Page
Format For Printing
XML
Clone This Bug