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
310806
[threaded-animations] REGRESSION: Flickery animations on
https://pudding.cool/2026/03/ivf/
https://bugs.webkit.org/show_bug.cgi?id=310806
Summary
[threaded-animations] REGRESSION: Flickery animations on https://pudding.cool...
Antoine Quint
Reported
2026-03-26 04:33:11 PDT
Load
https://pudding.cool/2026/03/ivf/
in STP 239 and click on "Parent" and notice how the two yellow bubbles that will appear on the right will not animate in as smoothly with the "Threaded Time-based Animations" flag enabled (the default) as it does when it's disabled.
Attachments
Flag disabled (GOOD)
(14.32 MB, video/quicktime)
2026-03-26 04:34 PDT
,
Antoine Quint
no flags
Details
Flag enabled (BAD)
(12.04 MB, video/quicktime)
2026-03-26 04:34 PDT
,
Antoine Quint
no flags
Details
Frame by frame
(438.80 KB, video/quicktime)
2026-03-26 04:41 PDT
,
Antoine Quint
no flags
Details
Reduction
(539 bytes, text/html)
2026-04-10 00:52 PDT
,
Antoine Quint
no flags
Details
Reduction
(1.16 KB, text/html)
2026-04-10 01:29 PDT
,
Antoine Quint
no flags
Details
Reduction
(862 bytes, text/html)
2026-04-10 01:41 PDT
,
Antoine Quint
no flags
Details
Show Obsolete
(2)
View All
Add attachment
proposed patch, testcase, etc.
Antoine Quint
Comment 1
2026-03-26 04:33:21 PDT
rdar://173154867
Antoine Quint
Comment 2
2026-03-26 04:34:36 PDT
Created
attachment 478804
[details]
Flag disabled (GOOD)
Antoine Quint
Comment 3
2026-03-26 04:34:52 PDT
Created
attachment 478805
[details]
Flag enabled (BAD)
Antoine Quint
Comment 4
2026-03-26 04:36:49 PDT
I meant the purple bubbles (not yellow).
Antoine Quint
Comment 5
2026-03-26 04:41:16 PDT
Created
attachment 478806
[details]
Frame by frame The issue is that we see a flash of the final frame before the animation runs, as shown in the attached frame-by-frame screen recording.
Antoine Quint
Comment 6
2026-03-26 23:38:04 PDT
Source code for the article is here:
https://github.com/the-pudding/ivf
.
Antoine Quint
Comment 7
2026-04-09 03:06:58 PDT
The issue goes away if I remove this line (
https://github.com/the-pudding/ivf/blob/925c270d44ab148eb3927d4e94e28bc0331a98a8/src/components/Html.svelte#L267
): delay: prefersReducedMotion.current ? 0 : DELAY + i * 250 Now I need to figure out what this "fly" directive does under the hood (
https://svelte.dev/docs/svelte/svelte-transition#fly
) in order to reduce the content to a simple test.
Antoine Quint
Comment 8
2026-04-09 03:45:34 PDT
This fly function is defined in
https://github.com/sveltejs/svelte/blob/7be1a0247f1ea84db2e951ab27716c68de5b0650/packages/svelte/src/transition/index.js#L79
and looks like this: const style = getComputedStyle(node); const target_opacity = +style.opacity; const transform = style.transform === 'none' ? '' : style.transform; const od = target_opacity * (1 - opacity); const [x_value, x_unit] = split_css_unit(x); const [y_value, y_unit] = split_css_unit(y); return { delay, duration, easing, css: (t, u) => ` transform: ${transform} translate(${(1 - t) * x_value}${x_unit}, ${(1 - t) * y_value}${y_unit}); opacity: ${target_opacity - od * u}` };
Antoine Quint
Comment 9
2026-04-09 13:14:38 PDT
This is going to be something to do with DOM mutations and transitions happening in quick succession.
Antoine Quint
Comment 10
2026-04-09 13:35:31 PDT
Some very surprising choices made by Svelte here :) First of all, the code above does not yield a CSS Transition but two JS-originated Web Animations. The first one is an animation that sets the base value for the duration of the delay. Then a second animation follows with 60 keyframes specified for each second the animation lasts! I guess this is so they have full control of how values are blended, but it's kind of overkill for a simple transition like the one I'm working with in my Svelte reduction… Haven't quite gotten to the point where I have a fairly simple reproduction, but I am getting closer!
Antoine Quint
Comment 11
2026-04-09 13:53:28 PDT
Here are links to the Svelte code: Delay animation:
https://github.com/sveltejs/svelte/blob/0e9e76f29262b5f64ac7a5d4db37ec83c9181634/packages/svelte/src/internal/client/dom/elements/transitions.js#L379
Active animation:
https://github.com/sveltejs/svelte/blob/0e9e76f29262b5f64ac7a5d4db37ec83c9181634/packages/svelte/src/internal/client/dom/elements/transitions.js#L440
The `onfinish` callback to chain those.
Antoine Quint
Comment 12
2026-04-09 14:28:52 PDT
Haven't quite managed to get it to reproduce as a standalone HTML file without using Svelte that could be used as a layout test, but I have enough to work on a fix.
Antoine Quint
Comment 13
2026-04-10 00:52:34 PDT
Created
attachment 478987
[details]
Reduction Attached reduction exhibits the problem fairly reliably with STP 240 with Threaded Time-based Animations enabled. Here's the basic code: const div = document.body.appendChild(document.createElement("div")); div.textContent = "Hello!"; const delay = div.animate({ opacity: [0, 0] }, 1500); delay.onfinish = () => div.animate({ opacity: [0, 1] }, { duration: 1000, fill: 'forwards' }); What's happening is that there's a moment where the remote layer tree still has the "delay" animation but the second animation is not started yet and we're flashing the element at opacity=1 because the delay animation has fill="remove".
Antoine Quint
Comment 14
2026-04-10 01:29:54 PDT
Created
attachment 478991
[details]
Reduction More deterministic reduction forcing the web process to be unresponsive for a while past the natural finish time for the initial animation.
Antoine Quint
Comment 15
2026-04-10 01:41:30 PDT
Created
attachment 478993
[details]
Reduction Better reduction still with a single animation.
Antoine Quint
Comment 16
2026-04-10 02:14:22 PDT
Here's how we approached this issue with the non-threaded code path in `GraphicsLayerCA::setupAnimation()`: ``` case GraphicsLayerAnimation::FillMode::None: fillMode = PlatformCAAnimation::FillModeType::Forwards; // Use "forwards" rather than "removed" because the style system will remove the animation when it is finished. This avoids a flash. break; ``` So perhaps we just need to make it so that we use a simulated "forwards" fill mode for removed animations in the remote layer tree. However, we must be careful here because this is coming from a world where additivity was not a thing. We must only do this for the cast where an animation is not composing with another animation in the stack.
Antoine Quint
Comment 17
2026-04-10 02:40:26 PDT
So simply forcing the fill to "forwards" for the remote layer tree animation addresses the issue both in the reduced test and on the live site, that's good! But I don't think we can simply do that in all cases where the fill-mode was specified as "none", we need to selectively determine whether that will create issue when animation effects are composed in the remote layer tree. We should be able to analyze this when a `RemoteAnimationStack` is created since we have all the timing information available to us, or perhaps do this on the web process side where much of the required surgery on effects is already performed.
Antoine Quint
Comment 18
2026-04-10 14:23:09 PDT
Pull request:
https://github.com/WebKit/WebKit/pull/62487
EWS
Comment 19
2026-04-10 23:43:19 PDT
Committed
310985@main
(ebfab2641e50): <
https://commits.webkit.org/310985@main
> Reviewed commits have been landed. Closing PR #62487 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