Animated cursor
Canvas-based cursor rendering with smooth movement and spring-damper smear trail. The cursor glides between positions and stretches into a smear shape during movement, matching the visual style of smear-cursor.nvim.
Fork mode recommended
The animated cursor works best with the bundled codemirror-vim fork (Obsidian’s built-in vim disabled). See recommended-setup.
Enable
Settings → Vim Motions → Animated cursor → Enable animated cursor
The animated cursor is disabled by default. When enabled, the plugin renders its own cursor on a <canvas> overlay and suppresses the fork’s vim cursor via the setCursorSuppressed API. Native CM6 cursor layers are always hidden by the fork regardless of animated cursor state — the fork renders its own cursor for every vim mode.
Smooth movement
When Smooth cursor movement is enabled, the cursor interpolates between positions using exponential decay instead of teleporting. The Cursor smoothness slider (0–1) controls how responsive the movement feels:
0.0— near-instant (83ms convergence)0.5— smooth (167ms convergence, default)0.9— dreamy (783ms convergence)
Smear trail
When Enable smear trail is enabled, the cursor shape deforms during movement using spring-damper physics on 4 corners. The leading corners arrive at the destination first while trailing corners lag behind, creating a stretching effect.
Configuration
| Setting | Default | Description |
|---|---|---|
| Trail stiffness | 0.6 | Head corner spring strength. Higher = snappier |
| Trail trailing stiffness | 0.3 | Tail corner spring strength. Lower = longer trail |
| Trail damping | 0.85 | Velocity decay. Lower = bouncier |
| Trail max length | 400px | Maximum trail length in pixels |
Cursor shapes
The animated cursor respects per-mode cursor shapes from Settings → Cursor shapes:
- Normal mode — block (filled rectangle with character overlay)
- Insert mode — bar (2px vertical line)
- Visual mode — block
- Replace mode — underline (2px horizontal line at bottom)
- Operator-pending — underline
Shape changes (e.g., entering insert mode) are instant — only position changes animate. Count-prefixed motions (4j, 2w) and multi-key motions (g$, gj) animate correctly — operator-pending detection is based on the actual operator state, not the chord display.
Cursor blinking
The animated cursor blinks matching CM6’s default behavior:
- 1200ms cycle — 600ms visible, 600ms hidden (hard on/off, no fade)
- 600ms reset delay — after any cursor movement, the cursor stays solid for 600ms before resuming blink
- Focus-aware — blink only runs when the editor has focus; unfocused editors show a solid cursor
- Suppressed during animation — while the smear trail or smooth movement is active, the cursor does not blink
Accessibility
The animated cursor respects the prefers-reduced-motion media query. When reduced motion is preferred, the cursor snaps instantly to its target position without animation.
Vimrc and Lua configuration
All animated cursor settings are available via vimrc and Lua. The option names use the smoothcursor prefix:
" Enable animated cursor
set smoothcursor
" Smooth movement (glide between positions)
set smoothcursorglide
set smoothcursorsmoothness=0.5
" Smear trail
set smoothcursorsmear
set smoothcursorstiffness=0.6
set smoothcursortrailstiffness=0.3
set smoothcursordamping=0.85
set smoothcursormaxlength=400-- Lua equivalent
vim.opt.smoothcursor = true
vim.opt.smoothcursorglide = true
vim.opt.smoothcursorsmoothness = 0.3
vim.opt.smoothcursorsmear = trueSee vimrc > Boolean options and lua-config > Supported vim.opt options for the full option reference.
Embeddable editors
The animated cursor renders in the oil explorer — the file manager editor shares the same canvas as regular editors.
Table cell editors, textarea vim overlays, footnote popovers, and modal editors use the native vim cursor (the fork’s BlockCursorPlugin) instead of the canvas. The global canvas (position: fixed on .app-container, z-index: 15) renders behind these elements due to higher z-index stacking contexts (e.g., popovers at z-index: 30). The native cursor — part of the editor’s own DOM — renders reliably above all content.
The CursorController detects editors inside .popover or .modal-container and automatically un-suppresses the fork’s cursor for those views. This covers Obsidian’s footnote editor, hover editors, and any third-party plugin editors created inside modals or popovers.
When navigating between table cells via h/j/k/l, a token-based position handoff seeds the new cell’s cursor controller with the previous cell’s screen position. The infrastructure for smooth cross-cell animation is in place, but the transition is not visible because the canvas draws behind cells. Within a single cell, cursor movement uses the native vim cursor’s standard rendering.
Known limitation
Cross-cell cursor movement snaps instead of animating. See known-limitations > Table cell vim modality for details.
Cross-platform resilience
The animation loop includes several defenses against platform-specific issues that can silently stop the cursor from rendering:
- Error recovery: A single bad frame (e.g., a transient null during window refocus) cannot kill the animation loop. Errors are caught and logged; the loop continues on the next frame.
- Heartbeat safety net: A background timer detects when the animation loop has stalled — due to OS-level throttling, Windows 11 Efficiency Mode, or sleep/wake transitions — and restarts it automatically.
- Visibility recovery: When the Obsidian window is hidden and restored (e.g., switching apps), the animation loop re-wakes immediately.
- Fractional DPI handling: Canvas dimensions are rounded to avoid sub-pixel artifacts on Windows displays with 125%/150% scaling.
Incompatibilities
The animated cursor is incompatible with other cursor animation plugins:
- ninja-cursor — does not detect the canvas cursor
- cursor-smith — both plugins try to render cursors
Disable these plugins when using the built-in animated cursor.
Tall line character positioning
On lines containing tall inline content (e.g., MathJax \dfrac expressions), the character beneath the block cursor previously shifted downward on some platforms and fonts. The renderer now uses the actual DOM character bounding rect for baseline calculation, which keeps the character at its correct vertical position regardless of line height.
Known limitations
See known-limitations > Animated cursor (smear + smooth movement).