Skip to content

editor: Smooth scrolling - #44827

Closed
marcocondrache wants to merge 93 commits into
zed-industries:mainfrom
marcocondrache:feat/smooth-scrolling
Closed

editor: Smooth scrolling#44827
marcocondrache wants to merge 93 commits into
zed-industries:mainfrom
marcocondrache:feat/smooth-scrolling

Conversation

@marcocondrache

@marcocondrache marcocondrache commented Dec 14, 2025

Copy link
Copy Markdown
Contributor

Closes #4355

Before:

zed-no-smooth.mp4

After:

zed.-.smooth.scroll.mp4

Ref: https://pavelfatin.com/scrolling-with-pleasure

Release Notes:

  • Editor now supports smooth scrolling

Signed-off-by: Marco Mihai Condrache <52580954+marcocondrache@users.noreply.github.com>
Signed-off-by: Marco Mihai Condrache <52580954+marcocondrache@users.noreply.github.com>
Signed-off-by: Marco Mihai Condrache <52580954+marcocondrache@users.noreply.github.com>
Signed-off-by: Marco Mihai Condrache <52580954+marcocondrache@users.noreply.github.com>
@cla-bot cla-bot Bot added the cla-signed The user has signed the Contributor License Agreement label Dec 14, 2025
@github-actions github-actions Bot added the community champion Issues filed by our amazing community champions! 🫶 label Dec 14, 2025
@github-project-automation github-project-automation Bot moved this to Community Champion PRs in Quality Week – December 2025 Dec 14, 2025
Signed-off-by: Marco Mihai Condrache <52580954+marcocondrache@users.noreply.github.com>
Signed-off-by: Marco Mihai Condrache <52580954+marcocondrache@users.noreply.github.com>
Signed-off-by: Marco Mihai Condrache <52580954+marcocondrache@users.noreply.github.com>
Signed-off-by: Marco Mihai Condrache <52580954+marcocondrache@users.noreply.github.com>
@MrSubidubi MrSubidubi self-assigned this Dec 15, 2025
Signed-off-by: Marco Mihai Condrache <52580954+marcocondrache@users.noreply.github.com>
Signed-off-by: Marco Mihai Condrache <52580954+marcocondrache@users.noreply.github.com>
@niekdomi

Copy link
Copy Markdown
Contributor

Thanks for this PR, feels smooth. Are there any plans to integrate this into vim-mode as well?

(e.g., ctr-y, ctrl-e, ctrl-b, ctrl-f, ctrl-d, ctrl-u, zz, zt, zb)

or is this not part of this PR?

@marcocondrache

Copy link
Copy Markdown
Contributor Author

@niekdomi yep, I still need to make the changes. I'm currently perfecting the scroll animation

Signed-off-by: Marco Mihai Condrache <52580954+marcocondrache@users.noreply.github.com>
Signed-off-by: Marco Mihai Condrache <52580954+marcocondrache@users.noreply.github.com>
Signed-off-by: Marco Mihai Condrache <52580954+marcocondrache@users.noreply.github.com>
the set_scroll_position of the editor saves to the db the position and
creates event which is wasteful for an animation

Signed-off-by: Marco Mihai Condrache <52580954+marcocondrache@users.noreply.github.com>
Comment thread crates/editor/src/element.rs Outdated
Signed-off-by: Marco Mihai Condrache <52580954+marcocondrache@users.noreply.github.com>

@ttytm ttytm left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Amazing effort!

Hope giving input already doesn't bother you.

Taking it through manual testing, I found that it doesn’t work with the optional preceding [count].

For example, my personal default for C+e / Ctrl+y is to scroll 5 lines. Currently it is reverted to 1 line when smooth scrolling is enabled.
No matter which scrolling motion is pressed [count] is ignored.

I wouldn’t want this to be a blocker, though. So a fix could also be part of a separate PR.

@marcocondrache

Copy link
Copy Markdown
Contributor Author

@ttytm, thank you for the feedback. I'm primarily focusing on improving the animation, especially for trackpads. I'm waiting for some input from the team before I make significant changes and dive deep into the vim crate

@marcocondrache
marcocondrache marked this pull request as ready for review December 16, 2025 13:43
slavpetroff added a commit to slavpetroff/zed that referenced this pull request Dec 18, 2025
@seanstrom

Copy link
Copy Markdown
Contributor

@marcocondrache when you mentioned detecting whether a device is "pre-smoothed" (macOS trackpad) or not (maybe standard mouse), is it possible to infer smooth scrolling from the scroll-delta and scroll-event frequency?

for example, it seems macOS trackpads emit a bunch of events compared to a mouse scroll-wheel, and the scroll-deltas are usually much bigger for a click-wheel mouse and much smaller for a macOS trackpad (since it emits many more events).

so could it be possible to sample the scroll device and guess if it's acting as a smoothed device?

@marcocondrache

Copy link
Copy Markdown
Contributor Author

@seanstrom As I mentioned in my previous message, this is not deterministic.

Some mice, such as Logitech devices, can emit scroll events with pixel-precise deltas, just like trackpads. With the information currently available, there is no reliable way to distinguish between them at the software level.

This is also reflected in the macOS documentation for hasPreciseScrollingDeltas, which explicitly states that both some mice and trackpads can provide precise scrolling deltas:

This property is valid for NSScrollWheel events. A generic scroll wheel issues rather coarse scroll deltas. Some mice and trackpads provide much more precise delta. This method determines how the values of the scrollingDeltaX and scrollingDeltaY should be interpreted.

So relying solely on event timing, delta precision, or event frequency is not reliable. High-DPI mice can emit as many events as, or even more events than, trackpads, especially when accounting for software-level enhancements.

Firefox takes a similar approach: smooth scrolling is enabled by default for both mouse wheel and pixel-precise scroll inputs, without relying on a mouse-vs-trackpad distinction.

@seanstrom

Copy link
Copy Markdown
Contributor

@marcocondrache thanks for clarifying and adding more context

I suppose I was wondering more whether if it's deterministic to detect pre-smoothing from a device, not necessarily whether a mouse or trackpad is being used. From what you mentioned, it seems like macOS provides a way to check if any device is providing precise deltas.

The reason I ask is because of the jittering issues that happen with trackpad scrolling on macOS and I was wondering whether there's a simpler path to avoid post-processing the already smooth events. Since that could side-step the jittering issues by allowing the OS to handle the smoothing. for example, we wouldn't use zed smooth scrolling for certain devices because they support hasPreciseScrollingDeltas and do not need more smoothing

Though you mentioned that jittering is expected behaviour in debug builds, and I'm not sure if you mean that it's a known bug to fix, or it's an implementation detail that we need to live with for debug builds. Could you explain more?

@marcocondrache

Copy link
Copy Markdown
Contributor Author

@seanstrom Jittering or lagging is expected in debug builds when smooth scrolling is enabled, mainly for performance reasons. This is similar to how opening the same file in release mode can feel fine, while in debug mode it may lag or become difficult to use. The two environments are not fully comparable from a performance perspective because release builds benefit from many compiler optimizations.

That said, the jittering you are currently experiencing could also be a regression caused by the fixes I recently made, so I do not want to dismiss it as only a debug-build performance issue.

Smooth scrolling is not lightweight, especially in Zed. We need to persist the scroll position, and the behavior also depends on the number of scroll events we receive. We also cannot rely on the OS to handle this for us, since Zed does not use native system components. We draw and compose everything ourselves using the OS graphics APIs.

A device supporting precise deltas does not mean that smoothing is being applied. It only means the device can report more precise movement values. If precise deltas were enough on their own, large applications like Chrome and Firefox would not need to implement smooth scrolling for those devices.

We also need to distinguish between actual regressions, jittering caused by debug-build performance, and other possible causes. Right now, there is no reliable way to ensure that smooth scrolling works correctly other than testing it directly. So if I make fixes in the vim-related code just to get some tests passing, there is a risk that I might introduce a visual bug in the animation or state handling.

That is why I am working on a more comprehensive test suite: to make sure we can catch regressions without accidentally breaking the visual behavior of smooth scrolling.

@seanstrom

seanstrom commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

@marcocondrache I see but I suppose when you mention:

A device supporting precise deltas does not mean that smoothing is being applied. It only means the device can report more precise movement values.

I thought hasPreciseScrollingDeltas is on the scroll event (at least on macOS), so it would be the hint that the values are already smoothed (since the docs mention using that flag to interpret the event data).

Also I'm not sure what you mean by needing to handle smoothing scrolling internally, since on macOS with latest stable Zed I get similar smooth-scrolling to what I see on VSCode. Am I missing something here?

@marcocondrache

marcocondrache commented Jun 19, 2026

Copy link
Copy Markdown
Contributor Author

@seanstrom Again, having precise delta events still doesn’t imply any kind of smoothing. Logitech mice can report precise deltas (so pixel based deltas) for every event while still scrolling line-by-line, so the resulting behavior is not smooth scrolling.

What the docs are saying is that we should interpret the event delta as raw pixel-based values rather than line-based values. See: https://github.com/marcocondrache/zed/blob/4640666e094d518bb622a6c172a35679bb92d1c8/crates/gpui_macos/src/events.rs#L272

@marcocondrache

marcocondrache commented Jun 19, 2026

Copy link
Copy Markdown
Contributor Author

@seanstrom Don’t get me wrong: when we have precise deltas most of the time, such as with macOS trackpads, we effectively already get continuous scrolling. That said, this does not necessarily apply to all devices or cases.

I think it depends on how we want to handle this. When I initially started this PR, I disabled smooth scrolling for precise-delta devices, but I later removed that change after realizing that my mouse was no longer getting smooth scrolling.

One option would be to provide a setting to disable it, similar to Firefox. Another would be to implement a best-effort classifier, like VS Code does, which tries to infer the device based on the raw values:

https://github.com/microsoft/vscode/blob/321cfba1d31e253c48ee91cd10199483b85a0c34/src/vs/base/browser/ui/scrollbar/scrollableElement.ts#L128

In any case, I would prefer to keep the classifier for a separate PR and, for now, agree on a sensible default while acknowledging that we may not cover smooth scrolling perfectly in all cases.

Happy to discuss this further. I’d also love to hear your opinion, @MrSubidubi

@MrSubidubi

Copy link
Copy Markdown
Member

I do not have a strong opinion in either direction, but

In any case, I would prefer to keep the classifier for a separate PR and, for now, agree on a sensible default while acknowledging that we may not cover smooth scrolling perfectly in all cases.

definitely sounds like a good idea overall. Happy to go with whatever you decice upon for this here.

@seanstrom

seanstrom commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

@marcocondrache yeah I can see now that the pixels values from the precise-deltas could be too large (not tiny enough for continuous smooth scrolling), so we need to smooth them out. That makes sense, thanks again for walking through that.

The classification approach is pretty interesting, I imagine that approach could allow for devices with native smooth scrolling to work without extra processing, though I can see your point about it complicating this PR.


One thing to double check, if you don't mind, is the behaviour for scrolling when the feature is disabled. I think either something changed for the default scrolling behaviour (or the settings toggle for smooth scrolling is inverted), because I'm only seeing jittering (for a release build) when I have the feature disabled.

I think I was testing with the most recent changes, here's a screen capture:

smooth-scrolling-zed.mov

Comment thread crates/editor/src/scroll.rs Outdated
Comment on lines +966 to +987
let behavior = behavior
.or(self
.smooth_scroll
.then_some(ScrollBehavior::RequestAnimation))
.unwrap_or_default();

if behavior == ScrollBehavior::Instant {
self.scroll_animation = Some(ScrollAnimation::Completed {
position: target_position,
});
return;
}

if self
.scroll_animation
.is_some_and(|a| a.target_position() == target_position)
{
return;
}

if let Some(animation) = &mut self.scroll_animation {
animation.restart(target_position);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Think the error might lie here @marcocondrache , from a quick glance before I'll sign off, we could have errors when !self.smooth_scroll and behavior == Some(ScrollBehavior::RequestAnimation) since then we'll not hit that unwrap_or_default, not the == Instant right after and might end up with that flickering. Might be wrong though.

Anyway, good find and think worth a test if that or something similar is the case

@marcocondrache marcocondrache Jun 20, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Nice catch indeed. I’ve fixed it, and I just need to add tests.

Unfortunately, it didn’t resolve the jittering issue when smooth scrolling is disabled, so I’ll need to dig deeper into that.

@marcocondrache

Copy link
Copy Markdown
Contributor Author

@seanstrom @xab3r The jittering should now be fixed.

@seanstrom

Copy link
Copy Markdown
Contributor

@marcocondrache thanks for the fixes, seems to be working well when tested on macOS with my trackpad (with both smooth-scrolling enabled and disabled).

Screen.Recording.2026-06-22.at.13.28.33.mov

Though I was testing with my Logitech MX vertical mouse (which I think also emits precise pixel-deltas when scrolling), and it seems like there's a little glitch when scrolling without smooth-scrolling enabled.

For example, occasionally when I do a full mouse-scroll by scrolling from edge-to-edge on the scroll-wheel, I notice that the end of my scroll has a weird "jump". It basically appears like I've finished scrolling and then suddenly jump to the real scroll position.

Here's a screen capture for reference (it occurs a few times in the video, but not every time):

Screen.Recording.2026-06-22.at.13.42.10.mov

@marcocondrache

Copy link
Copy Markdown
Contributor Author

@seanstrom Thanks for these reports. The jump should be fixed as well.

@seanstrom

Copy link
Copy Markdown
Contributor

@marcocondrache I've been testing the recent changes, and it doesn't seem like the issue is resolved, we may want to revert the latest changes unless you're sure removing the coalesce is important.

Based on my manual testing, I think I found a simple way to reproduce the issue, though it did require me to do a couple of tricks to recreate the glitch.

  • First thing I noticed is that I only could reproduce this glitch by toggling on/off the smooth-scroll feature. I would test scrolling with my trackpad and mouse, and eventually noticed the jump-glitch after toggling off smooth-scrolling.
  • Second thing I noticed is that I needed to toggle on smooth-scrolling, and scroll with my trackpad and mouse, but interrupt the scroll events of the trackpad with the scroll events of the mouse.
    • Then I would toggle off smooth-scrolling and repeat the process of scrolling with the trackpad and interrupting the trackpad scroll events with scroll events from the mouse.
    • Afterwards, just scrolling with mouse with smooth-scrolling disabled would cause the jumping-glitch to occur

My best guess here is that some of the smooth-scroll state is being persisted when toggled off, and perhaps it should be cleared (?). The good news is that it didn't seem to happen unless I toggled the smooth-scroll feature on and off and used both devices (trackpad and mouse) for scrolling.

@marcocondrache

marcocondrache commented Jun 24, 2026

Copy link
Copy Markdown
Contributor Author

@seanstrom Once smooth scrolling is disabled, it no longer interferes with normal scrolling, so toggling it on or off shouldn’t be the cause of the regressions we observed.

I’m no longer able to reproduce the jumping behavior when scrolling only with the mouse, which I was seeing before yesterday evening’s fix. I can reproduce it when scrolling with the trackpad and then interrupting those events with the mouse, but that appears to be an existing bug on Zed (tested with Zed Preview 1.8.2), not something introduced by this PR.

FYI @MrSubidubi

@xab3r

xab3r commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

@marcocondrache, there are still a few issues:

When the feature is enabled, the scrolling feels unnatural - very inertial rather than linear, not smooth. With a trackpad, I'd expect scrolling to track my finger 1:1, so I have full control over the speed. Only after I swipe and lift my finger off the trackpad should the scrolling keep going on its own, gradually slowing until it comes to a full stop within a second or so — with the exact duration depending on how fast I swiped.

The same goes for the mouse: as long as my finger is on the wheel, it feels more natural for the scroll to follow the wheel directly, without any added acceleration or smoothing.

When I disable the feature, though, I hit a regression. The flickering is gone now, but when I press a key, the scroll often lags or skips.

smooth_scroll scrolling using keyboard scrolling using trackpad
enabled ✅ works like a charm ❌ very inertial rather than linear
disabled ❌ a lot of lags or skips ✅ seems fine, no regression

settings.json:

"vim_mode": true,
"helix_mode": true

keymap.json:

"ctrl-k": [
  "action::Sequence",
  ["vim::LineDown", "vim::LineDown", "vim::LineDown", "vim::LineDown"]
]

Zed version: v1.9.x (ba134a0222) + the feature branch
macOS 26

@smitbarmase smitbarmase added the area:ui/scrolling Scrollbar and scrolling label Jun 30, 2026
jonx pushed a commit to jonx/zed-aros that referenced this pull request Jul 17, 2026
Make this only pub(crate) in preparation for
zed-industries#44827

Release Notes:

- N/A
@paperbenni

Copy link
Copy Markdown

😢

@theguy000

Copy link
Copy Markdown

damn

@aizigao

aizigao commented Aug 29, 2026

Copy link
Copy Markdown

A PR that has been under discussion for 10 months really makes it hard to have the courage to keep submitting PRs; many PRs for other issues are like this too

@theguy000

This comment was marked as off-topic.

@lhutyra

lhutyra commented Aug 29, 2026

Copy link
Copy Markdown

A PR that has been under discussion for 10 months really makes it hard to have the courage to keep submitting PRs; many PRs for other issues are like this too

I think all manpower went for delta dev, all zed is orphaned child. Many long awaited features, PRs became abandoned, lack of answears

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:ui/scrolling Scrollbar and scrolling cla-signed The user has signed the Contributor License Agreement community champion Issues filed by our amazing community champions! 🫶

Projects

No open projects
Status: Community Champion PRs

Development

Successfully merging this pull request may close these issues.

Smooth scrolling