1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
|
# Cursor shaders for ghostty
## WARNING: These are extremely customizable
## Demos
| Effect | Demo |
| -------- | ------ |
| Cursor Warp<br>(Neovide-like) |  |
| Cursor Sweep |  |
| Cursor Tail<br>(Kitty-like) |  |
| Ripple Cursor |  |
| Sonic Boom |  |
| Ripple Rectangle |  |
| Customized<br>(faded warp + ripple) |  |
## Trails
- [cursor_warp.glsl](cursor_warp.glsl): Neovide-like cursor trail, most customizable shader
- [cursor_sweep.glsl](cursor_sweep.glsl): Animated trail that shrinks from previous to current cursor position
- [cursor_tail.glsl](cursor_tail.glsl): Comet-like trail, mimicing kitty terminal's cursor_trail effect
## Pulse/Boom effects
- These trigger on cursor mode changes (block to line or vice versa, looks cool on changing modes in vim)
- [sonic_boom_cursor.glsl](sonic_boom_cursor.glsl): expanding filled circle
- [ripple_cursor.glsl](ripple_cursor.glsl): Expanding circular ring ripple effect
- [rectangle_boom_cursor.glsl](rectangle_boom_cursor.glsl): Same as boom_cursor but rectangular(cursor shape)
- [ripple_rectangle_cursor.glsl](ripple_rectangle_cursor.glsl): Same as ripple_cursor but rectangular(cursor shape)
> [!NOTE]
> If you have the line cursor (default), these effects will trigger and freeze on unfocus(as cursor changes to hollow block). The solution is to add `custom-shader-animation = always` to your ghostty config
## Usage
1. Clone the repo into your ghostty shaders directory:
```bash
git clone https://github.com/sahaj-b/ghostty-cursor-shaders ~/.config/ghostty/shaders
```
2. In your `~/.config/ghostty/config`, add:
```config
custom-shader = shaders/yourshader1.glsl
custom-shader = shaders/yourshader2.glsl
# ...
```
Replace `yourshader` with the name of any shader file (e.g., `cursor_sweep`, `ripple_cursor`, etc.)
## Customization
- All shaders has customizable parameters (like color, duration, size, thickness, etc) etc at the top of each file. You can adjust
- Also, all files has various **Easing Functions** to choose from.
- these function control the animation curve of the effects, you can make them elasitcy, springy, smooth, linear, etc by changing the easing function
- in trail shaders, you can comment/uncomment the easing functions in the code
- in pulse/boom shaders, you can comment/uncomment the lines in the `ANIMATION` section
- you can also add your own easing functions if you want
### Example (faded warp + ripple)
```glsl
// in cursor_warp.glsl
const float DURATION = 0.15;
const float TRAIL_SIZE = 0.8;
const float THRESHOLD_MIN_DISTANCE = 1.0;
const float BLUR = 1.0;
const float TRAIL_THICKNESS = 1.0;
const float TRAIL_THICKNESS_X = 0.9;
const float FADE_ENABLED = 1.0;
const float FADE_EXPONENT = 5.0;
```
```glsl
// in ripple_cursor.glsl
const float DURATION = 0.15;
const float MAX_RADIUS = 0.026;
const float RING_THICKNESS = 0.02;
const float CURSOR_WIDTH_CHANGE_THRESHOLD = 0.5;
vec4 COLOR = vec4(0.35, 0.36, 0.44, 0.8);
const float BLUR = 3.5;
const float ANIMATION_START_OFFSET = 0.01;
```
## Acknowledgements
Inspired by [Neovide](https://neovide.dev/) cursor animations and [KroneCorylus/ghostty-shader-playground](https://github.com/KroneCorylus/ghostty-shader-playground)
## License
MIT
## Why use branching(if/else) instead of branchless math
- coz we are dealing with **uniform branching** here, which has **NO DIVERGENCE**.
- ie, all fragments will take the same branch path, so no performance penalty on modern GPUs
- Branchless math would force GPU to calculate animations every single frame, even when there is no need
|