Non-Euclidean Scrollbar

← 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.

Normal Scrollbar
P
S
The first item becomes visible after scrolling up.
That item's layout is updated and the item is resized. The scrollbar then jumps up further.

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:

Non-Euclidean Scrollbar
db
da
p1
s1
p2
s2
The first item becomes visible after scrolling up. That item's layout is updated and the item is resized.
The scrollbar values are linearly interpolated using the formula below. P and S are the position and size of a normal scrollbar, and db and da are the distances to the top of the document before and after the scroll (both calculated after the items have been resized).

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:

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.