| ← Back to the algorithm list | Published on November 22nd, 2025 |
Long lists of variable-sized items inside a scroll view are often implemented using "virtualization" for performance, which typically means that layout is only updated for items that are currently visible on screen. The scroll view uses estimated sizes for other off-screen items until they become visible. However, that results in the scrollbar jumping around as item sizes are updated.
A non-Euclidean scrollbar can avoid jumping around as the scroll view of virtualized items is scrolled. It can do this by allowing the ratio of the scrollbar thumb to the scrollbar track to deviate from the ratio of the viewport to the scrollable area. I'm calling it "non-Euclidean" because distances are warped similar to non-Euclidean geometry.
The algorithm: When the scrollbar is scrolled, linearly interpolate the position and size of the scrollbar thumb from the current values toward the values for a normal scrollbar using the fraction of the remaining distance in the scroll direction that was just scrolled. See the diagram below and the example code for details.
Consider the following example scenario with a normal scrollbar where the user scrolls up to expose the first item. The rectangular outline is the viewport, which is what the user sees. Everything outside of the viewport is invisible to the user.
The normal scrollbar shown in blue above jumps when the view is scrolled up. The jump happens because the overall size of the document changes, which is because the first item enters the viewport and changes size due to a layout update.
To avoid this, the position and size of the non-Euclidean scrollbar (shown as p2 and s2 below) deliberately deviates from the position and size of the normal scrollbar (shown as P and S above). Notice how position p2 is less of an extreme change than position P:
t =
(db − da) ⁄
db
p2 =
p1 +
(P − p1) ·
t
s2 =
s1 +
(S −
s1) ·
t
The non-euclidean scrollbar shown in orange above smoothly interpolates toward the normal scrollbar position during the scroll to avoid a jump. Analogous math can be used for scrolling down instead of up, but using the distances to the bottom of the document instead of the top.
Demo
The live demo below shows this in action. The scrollable text document is represented as a list of paragraphs. Each paragraph starts with an estimated height of a single line. Paragraphs that become visible are resized to fit the viewport width, which updates the paragraph height accordingly. The rectangular outline is the viewport.
Some things to notice:
-
Slowly scroll down and pay attention to the faded paragraphs coming into the viewport. Paragraphs are only resized when they enter the viewport and become visible.
-
When scrolling with the mouse wheel, the normal scrollbar jumps whenever a paragraph is resized while the non-Euclidean scrollbar doesn't jump.
-
When dragging the normal scrollbar, the document jumps whenever a paragraph is resized. But when dragging the non-Euclidean scrollbar, the document doesn't jump during the drag.
-
Dragging the control to resize the viewport only updates the layout of paragraphs that are currently visible. All other paragraphs will be resized when they are next scrolled into view.
Notes
This algorithm is intended to be a guiding interaction design principle instead of a literal specification. Certain details require more work such as the "rubber band" elastic overflow scroll behavior sometimes used for touch screens. But these are natural extensions to the simple approach presented here. See the source code if you're curious (the demo above implements "rubber band" behavior for touch events).
The UI interaction presented here isn't necessarily finished. It just provides the primitive necessary to decouple scrollbar position from scroll position, which makes it possible to have natural scroll interactions without the performance cost of having to update the layout of the entire document in a single frame. It trades the weirdness of jumps during scrolling for the weirdness of a non-uniform scroll rate.
This UI could likely be improved even further by using extra idle CPU time to incrementally update the layout of the document in the background when necessary (such as when the viewport width changes or new paragraphs are added). That would help make the scroll rate more uniform in practice while still ensuring constant-time UI updates even for really big documents.