[charts] Stable progressive scatter batching + responsive zoom/pan#22862
Open
JCQuintas wants to merge 14 commits into
Open
[charts] Stable progressive scatter batching + responsive zoom/pan#22862JCQuintas wants to merge 14 commits into
JCQuintas wants to merge 14 commits into
Conversation
Only the first level of the progressive scatter render is ever visible during a zoom/pan interaction, and the per-frame work is minimized: - `selectorProgressiveSeriesRevealedBatches` clamps the revealed batches to the first level while interacting, so no frame can show more than the first level regardless of how far the reveal had progressed. - The scheduler resets the revealed rounds to the first level and pauses while interacting, then resumes the progressive fill from there once the interaction settles (after a short inactivity delay). - `ScatterAsync` mounts only the first batch while interacting; the other batches would otherwise render an empty `<g>` yet still re-render every frame, since their store subscription bypasses `React.memo`. - `ScatterAsyncBatch` skips the per-marker highlight state and interaction handlers while interacting; they are useless mid-drag and were the dominant per-frame cost for large datasets.
While zooming/panning, render only a short stable dataIndex prefix (INTERACTION_POINT_BUDGET points per series) for the first level instead of the full batch, cutting per-frame element reconciliation. The rest fills in once the interaction settles.
Deploy previewBundle size
PerformanceTotal duration: 2,137.99 ms +215.05 ms(+11.2%) | Renders: 63 (+0)
23 tests within noise — details Metric alarms
Check out the code infra dashboard for more information about this PR. |
…tion Collapsing the revealed rounds to the first level on every zoom/pan hid already-painted points for the whole gesture and forced a settle- delayed re-reveal on each pan, flickering on repeated panning. Skip the collapse when the wave is already complete; ScatterAsync still caps the mounted batches while interacting, so the gesture stays cheap.
…l clamp Extract pure helpers (packScatterSeriesCoords, getRevealedBatchCount) from the selectors so the dataIndex-indexed packing, visibility flag, batch-view range math, and interaction clamp are covered by unit tests.
JCQuintas
commented
Jun 17, 2026
Comment on lines
+87
to
+92
| export function packScatterSeriesCoords( | ||
| data: readonly { x: number | Date; y: number | Date }[], | ||
| getXPosition: (value: number | Date) => number, | ||
| getYPosition: (value: number | Date) => number, | ||
| bounds: ScatterVisibilityBounds, | ||
| ): ScatterSeriesRenderData { |
Member
Author
There was a problem hiding this comment.
This is ever so slightly less speeeeed than the previous iteration.
New implementation stores coords for all items instead of only visible range, but since interacting got faster (due to rendering only first batch), this is technically a net improvement
This "architectural" change is what makes the panning stable when zoomed in.
You can check an "unstable" panning on the PR in #22817
Unstable
Screen.Recording.2026-06-17.at.16.50.22.mov
Stable
Screen.Recording.2026-06-17.at.16.51.52.mov
1 task
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Progressive scatter: stable batching + responsive zoom/pan
Builds on the progressive scatter renderer to fix point popping while panning and keep interactions responsive.
Fix: points popping in/out while panning
Root cause:
ScatterAsyncbatched over the viewport-filtered coords array (batch membership = how many lower-dataIndexpoints were currently visible), while the scheduler sizes batches over the total point count. As the visible set shifted each pan frame, a point's batch changed → mid-screen popping.Fix: batch over
dataIndexranges and decide visibility per point at render time.scatterRenderData.selectors: packedFloat64Arrayis now indexed bydataIndex, stride 3[x, y, visible]. Off-screen points keep their slot instead of being dropped, so a point's batch is fixed across zoom/pan.ScatterAsyncBatch: skips points flagged invisible;dataIndex = start + local.ScatterAsync: batch count is total-based, matching the scheduler.Perf: cheaper interaction frames
While interacting, the first level is capped to a short stable
dataIndexprefix (INTERACTION_POINT_BUDGETper series) instead of the full batch, cutting per-frame<circle>reconciliation. The rest fills in once the interaction settles. Stable prefix = no popping; a uniform sample for unsorted data.Docs
Progressive scatter demo is now zoomable to exercise the interaction path.
Closes #22817
Closes #22731