Skip to content

Commit b6ff8d8

Browse files
nhelfmanNoam Helfman
andauthored
Scroll Timing API proposal (#1222)
* initial commit - scroll timing * first pass in adapting to standard explainer.md format * Enhance explainer.md with author details, participation links, and structured table of contents; outline goals and non-goals for the Scroll Timing Performance API. * Add user research references and clarify dependencies in explainer.md * Document alternatives considered for the Scroll Timing Performance API and clarify accessibility, internationalization, privacy, and security considerations. * minor tweaks. * Refactor smoothness score calculation for clarity and consistency in demo.html and polyfill.js * Implement manual smooth scrolling for consistent behavior across browsers in demo.html * added acknowledgements * Load polyfill conditionally based on native PerformanceScrollTiming support * Enhance demo.html to load polyfill asynchronously and dispatch event when API is ready * Clarify PerformanceScrollTiming interface by updating the 'name' attribute description to indicate it always returns "scroll" * Update checkerboardTime attribute type to DOMHighResTimeStamp in PerformanceScrollTiming interface * clarified startTime for programmatic scroll * Add scrollSource classification discussion for fragment navigation and focus-driven scrolling * Clarify discussion on adding "scrollbar" as a distinct scrollSource value, addressing privacy concerns and diagnostic value. * Refactor scroll distance attributes from distanceX/distanceY to deltaX/deltaY across documentation and implementation for consistency in the PerformanceScrollTiming API. * Clarify `name` attribute description in PerformanceScrollTiming interface to specify it always returns "scroll" * Add scrollbar scrolls to velocity-related patterns in design notes --------- Co-authored-by: Noam Helfman <noamh@microsoft.com>
1 parent 79cad29 commit b6ff8d8

5 files changed

Lines changed: 2784 additions & 0 deletions

File tree

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
# Design Notes
2+
3+
Scroll performance encompasses several measurable aspects that together determine the quality of the scrolling experience:
4+
5+
- **Responsiveness**: How quickly the page responds when a user initiates a scroll gesture
6+
- **Smoothness**: Whether frames are rendered consistently at the target frame rate during scrolling
7+
- **Visual Completeness**: Whether content is fully painted when it scrolls into view
8+
- **Stability**: Whether the scroll position remains predictable without unexpected jumps
9+
- **Velocity**: How fast the user is scrolling, which helps contextualize performance metrics
10+
11+
## Scroll Start Time
12+
Scroll start time measures the latency between the user's scroll input and the first visual update on screen.
13+
14+
**Key metrics:**
15+
- **Input timestamp**: When the scroll gesture was detected (touch, wheel, or keyboard event)
16+
- **First frame timestamp**: When the first frame reflecting the scroll was presented
17+
- **Scroll start latency**: The delta between input and first frame presentation
18+
19+
In this proposal:
20+
- `entry.startTime` is the **input timestamp** (the first input in the scroll sequence).
21+
- `entry.firstFrameTime` is the **first frame timestamp**.
22+
- `scrollStartLatency` can be derived as `entry.firstFrameTime - entry.startTime`.
23+
24+
**Why it matters:**
25+
High scroll start latency makes the page feel unresponsive. Users expect immediate visual feedback when they initiate a scroll gesture. Latency greater than 100ms is generally perceptible and negatively impacts user experience.
26+
27+
**Common causes of high scroll start latency:**
28+
- Long-running JavaScript blocking the main thread
29+
- Expensive style recalculations or layout operations
30+
- Compositor thread contention
31+
- Touch event handlers without `{ passive: true }`
32+
33+
## Scroll End Time
34+
Scroll end time captures when a scroll interaction completes and the viewport settles at its final position.
35+
36+
**Key metrics:**
37+
- **Last input timestamp**: The final scroll input event in a scroll sequence
38+
- **Settle timestamp**: When momentum/inertia scrolling completes and the viewport is stable
39+
- **Total scroll duration**: Time from scroll start until scrolling stops (includes momentum/inertia)
40+
41+
**Why it matters:**
42+
Understanding scroll end time is essential for:
43+
- Measuring total scroll interaction duration
44+
- Triggering deferred work (lazy loading, analytics) at the right moment
45+
- Calculating overall scroll responsiveness metrics
46+
47+
**Considerations:**
48+
- Momentum scrolling on touch devices extends scroll duration beyond the last touch input
49+
- Programmatic smooth scrolling has predictable end times
50+
- Scroll snapping may adjust the final position after user input ends
51+
52+
## Scroll Smoothness
53+
Scroll smoothness measures how consistently frames are rendered during a scroll animation, reflecting visual fluidity.
54+
55+
**Key metrics:**
56+
- **Frames expected**: Number of frames that should have been rendered at the target refresh rate
57+
- **Frames produced**: Number of frames actually rendered
58+
- **Dropped frame count**: Frames that were skipped or missed their deadline
59+
- **Average frame duration**: Mean time between presented frames
60+
- **Frame duration variance**: Consistency of frame timing (lower is smoother)
61+
- **Smoothness percentage**: `(frames_produced / frames_expected) * 100`
62+
63+
**Why it matters:**
64+
Even if scroll starts quickly, dropped frames during scrolling create visible jank. Users perceive scroll smoothness as a key quality indicator. A smoothness score below 90% is typically noticeable, and below 60% is considered poor.
65+
66+
**Common causes of scroll jank:**
67+
- Expensive paint operations (large areas, complex effects)
68+
- Layout thrashing during scroll event handlers
69+
- Non-composited animations
70+
- Image decoding on the main thread
71+
- Garbage collection pauses
72+
73+
## Scroll Checkerboarding
74+
Scroll checkerboarding occurs when content is not ready to be displayed as it scrolls into the viewport, resulting in blank or placeholder areas.
75+
76+
**Key metric:**
77+
- **Checkerboard time**: Total duration (ms) that unpainted areas were visible during scroll
78+
79+
**Why it matters:**
80+
Checkerboarding breaks the illusion of scrolling through continuous content. It's particularly problematic for:
81+
- Image-heavy pages where images load as they scroll into view
82+
- Infinite scroll implementations
83+
- Complex layouts with off-screen content
84+
- Pages with web fonts that haven't loaded
85+
86+
**Common causes:**
87+
- Slow network preventing timely resource loading
88+
- Insufficient tile/layer rasterization ahead of scroll
89+
- Large images without proper sizing hints
90+
- Lazy loading triggered too late
91+
92+
**Note:** The API currently includes `checkerboardTime` to measure the duration of checkerboarding. A potential future addition is a metric for checkerboard *area* (what percentage of the viewport was affected). See [OPEN_QUESTIONS.md](OPEN_QUESTIONS.md#checkerboard-area-aggregation-method) for discussion of area aggregation approaches.
93+
94+
## Scroll Velocity
95+
Scroll velocity measures the speed at which a user navigates through content, calculated as the distance scrolled divided by the duration of the scroll interaction.
96+
97+
**Key metrics:**
98+
- **Scroll distance components**: Horizontal and vertical scroll deltas (`entry.deltaX`, `entry.deltaY`)
99+
- **Total scroll distance**: Euclidean distance combining both axes: `√(deltaX² + deltaY²)`
100+
- **Scroll duration**: Time from scroll start to scroll end (`entry.duration`)
101+
- **Average velocity**: `totalDistance / duration` (pixels per millisecond, or multiply by 1000 for pixels per second)
102+
- **Directional velocity**: Calculate velocity separately for X and Y axes to understand scroll direction and bias
103+
- **Peak velocity**: Maximum instantaneous scroll speed (requires sampling during interaction)
104+
105+
**Why it matters:**
106+
Understanding scroll velocity is essential for performance optimization because different scroll speeds reveal different performance characteristics. Since PerformanceObserver reports entries asynchronously after scroll completion, velocity data is primarily used for telemetry, diagnostics, and informing optimization decisions rather than real-time adaptation.
107+
108+
1. **Performance Issue Diagnosis**: Jank often correlates with scroll velocity. Telemetry may show that a page performs smoothly at low speeds but exhibits dropped frames at high velocities due to:
109+
- Insufficient rasterization ahead of the scroll direction
110+
- Paint operations that can't keep up with scroll speed
111+
- Layout recalculations triggered by scroll position-dependent logic
112+
113+
By analyzing velocity alongside smoothness metrics in RUM data, developers can identify velocity thresholds where performance degrades and optimize accordingly.
114+
115+
2. **User Intent Inference**: Scroll velocity provides context for interpreting other performance metrics:
116+
- High velocity + high smoothness = well-optimized scrolling at scale
117+
- High velocity + low smoothness = performance bottleneck under stress
118+
- Low velocity + low smoothness = fundamental rendering issues even for gentle scrolling
119+
- Very high velocity = user skimming or navigating, may not be engaging deeply with content
120+
121+
This helps prioritize which performance issues to address based on actual user behavior patterns.
122+
123+
3. **Interaction Quality Scoring**: For metrics aggregation and percentile analysis, weighting by velocity helps identify the most impactful performance issues. A jank during a fast 5000px scroll (user actively navigating) may have different implications than jank during a tiny 50px adjustment. Velocity data allows developers to segment and analyze performance by scroll intensity.
124+
125+
4. **Optimization Strategy Validation**: By collecting velocity-stratified performance data, developers can:
126+
- Validate whether optimizations improve performance across all velocity ranges
127+
- Identify if certain architectural decisions (lazy loading strategies, virtualization approaches, paint complexity) work well for typical scroll speeds but fail at extremes
128+
- Make informed tradeoffs (e.g., "our current implementation handles 95% of scroll velocities smoothly")
129+
130+
5. **Benchmarking and Regression Detection**: Velocity provides a standardized dimension for performance testing. Developers can establish performance baselines across velocity buckets (slow: <1000 px/s, medium: 1000-3000 px/s, fast: >3000 px/s) and detect regressions when new code degrades smoothness at specific velocity ranges.
131+
132+
**Common velocity-related patterns:**
133+
- **Fling scrolls**: Touch flings on mobile often produce high initial velocity that decays over time (momentum scrolling)
134+
- **Keyboard/wheel scrolls**: Usually lower, more consistent velocity with discrete steps
135+
- **Scrollbar scrolls**: Dragging the scrollbar thumb can jump through large distances quickly and tends to produce the most checkerboarding, according to Chromium metrics
136+
- **Programmatic scrolls**: Smooth scroll behavior produces predictable, constant velocity
137+
- **Search navigation**: Users jumping to search results often produce short-duration, high-velocity scrolls
138+
139+
## Scroll Interruption and Cancellation
140+
141+
Scroll interactions can be interrupted or cancelled mid-stream. This section defines how `PerformanceScrollTiming` entries behave in these scenarios.
142+
143+
**Scenarios:**
144+
145+
1. **Touch lift during momentum**: User initiates a touch fling, then lifts finger. Momentum scrolling continues until friction stops it or the user touches again.
146+
- The entry covers the entire interaction including momentum phase
147+
- `duration` extends until scrolling stops (not when the finger lifts)
148+
149+
2. **Programmatic interruption**: A `scrollTo()` or `scrollIntoView()` call interrupts an ongoing user scroll.
150+
- The user-initiated scroll entry ends at the interruption point
151+
- A separate entry with `scrollSource: "programmatic"` may be emitted for the programmatic scroll
152+
153+
3. **Input source switch**: User starts scrolling with touch, then uses mouse wheel mid-scroll.
154+
- The original entry ends when the new input source is detected
155+
- A new entry begins for the new input source
156+
- `scrollSource` reflects the initiating input for each entry
157+
158+
4. **Scroll snap adjustment**: After user input ends, CSS scroll snapping moves the viewport to a snap point.
159+
- Snap adjustment is considered part of the same scroll interaction
160+
- `duration` includes the snap animation time
161+
162+
5. **Boundary collision**: Scroll reaches container bounds and cannot continue.
163+
- Entry ends naturally when scrolling stops at the boundary
164+
- Overscroll/bounce effects (on supported platforms) are included in `duration`
165+
166+
**Entry emission timing:**
167+
Entries are emitted after the scroll interaction fully completes (including momentum, snap, and settle phases). Interrupted scrolls emit entries at the interruption point with metrics reflecting the partial interaction.
168+
169+
## Edge Cases
170+
171+
This section documents expected behavior for boundary conditions and unusual scenarios.
172+
173+
**Very short scrolls (`framesExpected` = 0):**
174+
- If a scroll interaction completes within a single frame, `framesExpected` may be 0 or 1
175+
- Implementations should avoid division-by-zero when calculating smoothness: treat `framesExpected = 0` as 100% smooth
176+
- Short scrolls are still valid entries and should be emitted
177+
178+
**Zero scroll distance:**
179+
- User attempts to scroll at a boundary (already at top/bottom)
180+
- `deltaX` and `deltaY` are both 0
181+
- Entry is still emitted (the interaction occurred, even if no visual change resulted)
182+
- Useful for detecting "frustrated scrolling" at boundaries
183+
184+
**Overscroll and bounce effects:**
185+
- On platforms with overscroll (iOS rubber-banding, Android overscroll glow):
186+
- `deltaX`/`deltaY` reflect the actual scroll position change, not the visual overscroll
187+
- `duration` includes the bounce-back animation time
188+
- Overscroll does not count as checkerboarding
189+
190+
**Scroll-linked animations:**
191+
- If the page uses `scroll-timeline` or JavaScript scroll-linked animations:
192+
- Performance of those animations is not directly captured by this API
193+
- Frame metrics reflect the scroll's visual update, not dependent animations
194+
- Consider using separate performance instrumentation for scroll-linked effects
195+
196+
**Rapid repeated scrolls:**
197+
- Quick successive scroll gestures (e.g., rapid wheel clicks) may:
198+
- Merge into a single entry if within the scroll-end detection window
199+
- Emit separate entries if separated by sufficient idle time
200+
- Implementation defines the debounce/merge behavior
201+
202+
**Disabled scrolling:**
203+
- If `overflow: hidden` prevents scrolling, no entry is emitted (no scroll occurred)
204+
- If JavaScript prevents default on scroll events, behavior is implementation-defined

0 commit comments

Comments
 (0)