Skip to content

Conversation

@nhelfman
Copy link
Contributor

@nhelfman nhelfman commented Jan 6, 2026

Initial proposal for performance Scroll Timing API.
Besides the short explainer, included also design notes document, open question document and a demo with polyfill.

@nhelfman nhelfman marked this pull request as ready for review January 6, 2026 08:33
@aluhrs13
Copy link
Contributor

aluhrs13 commented Jan 7, 2026

Can you add "Authors" and "Participate" sections? https://github.com/w3ctag/explainer-explainer/blob/main/template.md

@nhelfman
Copy link
Contributor Author

Can you add "Authors" and "Participate" sections? https://github.com/w3ctag/explainer-explainer/blob/main/template.md

Done. See latest iteration.

Scroll Timing Performance API

A proposal to standardize scroll performance measurement on the web.

Authors

Participate

readonly attribute DOMHighResTimeStamp duration;
readonly attribute unsigned long framesExpected;
readonly attribute unsigned long framesProduced;
readonly attribute double checkerboardTime;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not DOMHighResTimeStamp for checkerboardTime so that the unit (ms) is baked into the type?

Copy link
Contributor Author

@nhelfman nhelfman Jan 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right. Updated.

Comment on lines 97 to 98
readonly attribute long distanceX;
readonly attribute long distanceY;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT on naming - 'distance' implies a non-negative number. What about deltaX like the wheel event, or scrollX?

Copy link
Contributor Author

@nhelfman nhelfman Jan 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense. Updated.

interface PerformanceScrollTiming : PerformanceEntry {
// Inherited from PerformanceEntry
readonly attribute DOMString entryType; // Always "scroll"
readonly attribute DOMString name; // Empty string
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on the name values returned by other sub-classes of PerformanceEntry, it sounds like "scroll" would be more appropriate than an empty string.

https://developer.mozilla.org/en-US/docs/Web/API/PerformanceEntry/name

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense. It is only set empty for LargestContentfulPaint. Perhaps if we will have different scroll metrics that can be produced we would need different name. I'll set it to scroll.

|-----------|------|-------------|
| `entryType` | DOMString | Always `"scroll"` (inherited from PerformanceEntry) |
| `name` | DOMString | Empty string (inherited from PerformanceEntry) |
| `startTime` | DOMHighResTimeStamp | Timestamp of the first input event that initiated the scroll |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm assuming here, but I would guess "or 0 for programmatic invocation" is the intention? If so, consider adding that as a callout if you don't think it's makes it too long.

Copy link
Contributor Author

@nhelfman nhelfman Jan 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My thinking was the startTime for programmatic scrolls should be the timestamp of the scroll invocation (e.g. time of scrollTo call).

Updated the description.

| `checkerboardTime` | double | Total duration (ms) that unpainted areas were visible during scroll |
| `distanceX` | long | Horizontal scroll distance in pixels (positive = right, negative = left) |
| `distanceY` | long | Vertical scroll distance in pixels (positive = down, negative = up) |
| `scrollSource` | DOMString | Input method: `"touch"`, `"wheel"`, `"keyboard"`, `"other"`, or `"programmatic"` |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly just curious, but what value gets used when scrolling to a header, via a # in the URL?

I'm thinking primarily about the typical table-of-contents pattern where the user might click on a link and get scrolled down the page - would it make sense to make "mouse" an option for that, rather than "other", given the widespread usage of that design pattern? And if they did it via keyboard would it come through as "keyboard"? (I ask this one more from glancing in the polyfill and seeing a listed subset of keys that doesn't include things like 'enter').

Related - if tabbing through items on a page, will that go down as "keyboard"?

Copy link
Contributor Author

@nhelfman nhelfman Jan 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good questions. I have not considered these scenarios yet. Updated the "open questions section".

| `name` | DOMString | Empty string (inherited from PerformanceEntry) |
| `startTime` | DOMHighResTimeStamp | Timestamp of the first input event that initiated the scroll |
| `firstFrameTime` | DOMHighResTimeStamp | Timestamp when the first visual frame reflecting the scroll was presented |
| `duration` | DOMHighResTimeStamp | Total scroll duration from `startTime` until scrolling stops (includes momentum/inertia) |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And if startTime is 0 for programmatic invocations, what about duration being based on firstFrameTime instead? If devs want to include the initial lag time as well they can check for a non-zero startTime and then add the firstFrameTime - startTime diff.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated the description.

- **Keyboard/wheel scrolls**: Usually lower, more consistent velocity with discrete steps
- **Programmatic scrolls**: Smooth scroll behavior produces predictable, constant velocity
- **Search navigation**: Users jumping to search results often produce short-duration, high-velocity scrolls

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's worth mentioning scrollbar scrolls, they produce the most checkerboarding, according to Chromium metrics.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's worth mentioning scrollbar scrolls, they produce the most checkerboarding, according to Chromium metrics.

Added a bullet capturing this.

readonly attribute DOMHighResTimeStamp startTime;
readonly attribute DOMHighResTimeStamp firstFrameTime;
readonly attribute DOMHighResTimeStamp duration;
readonly attribute unsigned long framesExpected;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Smoothness metrics, such as framesExpected, framesProduced, and checkerboardTime, provide meaningful measurements of smoothness across various animations and interactions scenarios, including scroll-driven animations and JavaScript/CSS-based animations. While implementing a full Animation Smoothness API is not the immediate goal, introducing these metrics is a significant step toward measuring key aspects of animation performance and user-perceived smoothness.

I’m curious if you’ve considered separating generic smoothness attributes from PerformanceScrollTiming into a distinct interface. This approach allows us to address immediate needs while laying the groundwork for potential future API development.

interface AnimationSmoothnessEntry : PerformanceEntry {
  readonly attribute unsigned long framesExpected;
  readonly attribute unsigned long framesProduced;
  readonly attribute double checkerboardTime;
}

interface PerformanceScrollTiming : AnimationSmoothnessEntry {
...
}

Copy link
Contributor Author

@nhelfman nhelfman Jan 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea. If there is a consensus that the path for Animation Smoothness API will include something like this then it certainly reasonable to structure it like this.

There is still an orthogonal open question on what expectedFrame should mean. In my prototype I measure the v-sync rate and use that as the expected so if the frame rate changes the expected changes as well. However, it may be that it makes more sense to decide on a constant smoothness frame rate (e.g. 60 FPS) and use that to calculate the expectedFrames value.

I can update to the proposal

| `checkerboardTime` | double | Total duration (ms) that unpainted areas were visible during scroll |
| `distanceX` | long | Horizontal scroll distance in pixels (positive = right, negative = left) |
| `distanceY` | long | Vertical scroll distance in pixels (positive = down, negative = up) |
| `scrollSource` | DOMString | Input method: `"touch"`, `"wheel"`, `"keyboard"`, `"other"`, or `"programmatic"` |
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the API should be able to report scrollbar scrolls, as they are the most common source of checkerboarding.

Copy link
Contributor Author

@nhelfman nhelfman Jan 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is indeed my preference. Some feedback that was raised is that providing explicit "scrollbar" (or perhaps "trackpad", "autoscroll" etc.) might be a privacy concern since it exposed more information than currently is exposed. To mitigate this upfront I grouped all of them to the "other" since that does not expose any additional information that wasn't existing before. We should have a discussion about this concern and perhaps can think of mitigations which will alleviate the privacy concern.

Expanded description in the open questions "Scrollbar as a Distinct Scroll Source" section capturing this.

@mwjacksonmsft mwjacksonmsft self-assigned this Jan 15, 2026
@mwjacksonmsft mwjacksonmsft merged commit b6ff8d8 into MicrosoftEdge:main Jan 15, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants