Repository navigation
Click effects land off the drawn cursor under cursor smoothing #1335
Description
Problem
With the default cursor smoothing (0.67), the flat cursor sprite trails the recorded pointer
by about 190 ms. Every click effect is anchored to the raw click position, so when a click
follows a move closely, the effects land beside the cursor:
- the impact ring and the click shader effects (
ctx.clicks) sit on the raw point; ctx.cursor, given to the same effects, sits on the lagging smoothed point, so an effect
reading both sees them disagree;- auto zooms frame the raw click while the cursor arrives late.
It shows most on scripted takes (eased move, click about 100 ms after the stop) and on fast
human clicks. A human who dwells before clicking hides it: the spring has caught up.
Root cause
crates/compositor/src/frame_geometry.rs, CursorPlacer::at:
fn at(&self, t: f32) -> Option<(f32, f32)> {
if self.model3d { self.track.pinned_at(t) } else { self.track.at(t) }
}The 3D cursor already converges on the click (CursorTrack::pinned_at: eases onto the raw click
point over 250 ms, holds it 100 ms, releases over 250 ms). The flat sprite, the default, skips
it and draws track.at(t), the smoothed spring output.
Measurement
Exact port of CursorTrack::smoothed() (240 Hz spring) on two real sidecars, 1920×1080 output.
Distance between the drawn cursor and the click point, at the click.
Demo take (Playwright, eased move, 2 clicks)
| smoothing | spring lag (c/k) | offset at click | max offset while moving |
|---|---|---|---|
| 0 | 0 | 0 px | 0 px |
| 0.1 | 57 ms | 3 px | 209 px |
| 0.3 | 95 ms | 18 to 22 px | 328 px |
| 0.5 | 171 ms | 85 to 119 px | 499 px |
| 0.67 (default) | 189 ms | 106 to 150 px | 534 px |
| 1 | 233 ms | 155 to 224 px | 602 px |
Human take (WGC capture, dwell before each click): 0 px at click for every value.
Also noted: the curve jumps at 0 (passthrough, then 45 ms of lag at 0.01), and the slider's
upper half only covers a third of the spring's extended range.
Proposed fix
- Pin the flat sprite too.
CursorPlacer::atalways returnspinned_at(t). The drawn
cursor,ctx.cursor,ctx.clicks, the bounce and the impact ring then meet at the click,
at any smoothing value. One line, both renderers share it, andpinned_atalready has its
test (pinned_atlands on the raw point at 0, 0.5 and 1). - Close clicks each keep the tip.
pinned_atstarts a click's approach only after the
previous click's hold: with clicks 200 ms apart, the next approach pulled the tip off the
first click at its own instant. - Lower the default from 0.67 to 0.3 (
src/lib/projectDefaults.ts), for new projects
only: about 95 ms of lag, still removes capture tremor. Saved projects keep their value.
Not in scope: the auto-follow camera keeps its own damping (follow_at), by design.
Activity
EtienneLescot commentedon Oct 21, 2026
Fixed on main in #1336: the flat cursor now lands on each click like the 3D cursor, so the drawn tip, the impact ring and
ctx.clicksmeet whatever the smoothing, and new projects start at 0.3 smoothing. It will ship with the next release. Thanks for the report!