[Media] Catsor: a procedurally animated desktop cat in Rust, with no GUI toolkit (tiny-skia + x11rb / windows-sys)

124 points PilotZealousideal470 18 comments
[Media] Catsor: a procedurally animated desktop cat in Rust, with no GUI toolkit (tiny-skia + x11rb / windows-sys)

Catsor is a small cat that lives on your desktop and follows your mouse. Wiggle the cursor like a laser pointer and it stalks and pounces. Shake the mouse and it gallops over. Leave it alone and it loafs, then falls asleep. It runs on Linux/X11 and Windows as a single binary.

Repo: https://github.com/ozanenginoglu/catsor (Apache-2.0)

Some details that might interest people here:

  • No sprites. The cat is a 12-node world-space chain (head, spine, tail) plus 4 planted paws. Every frame it solves the spine and tail, then tiny-skia builds the body around the skeleton with paths. Paws follow real 4-beat walk, trot and rotary-gallop phase schedules.
  • Carrier vs. displayed spine. The body follows a smooth "carrier" path. Gait wobble, head bob and butt-wiggle are added on top as offsets that never feed back into it. Before I split them, the cat slowly drifted off course and twisted its spine.
  • Oscillators are kept as running phases, not time * freq. After a few hours of uptime, f32 precision makes time * freq stutter, and changing the frequency makes the phase jump. There are tests that simulate long uptimes to catch this.
  • Deterministic simulation. CatSim never touches the OS and has its own seeded RNG. The tests are behavioral: they feed synthetic cursor paths at a fixed dt and assert on activity, positions and smoothness. The README's demo GIF is rendered the same way, from a scripted cursor driving the sim.
  • Small dependency tree: tiny-skia, glam, clap, plus x11rb (pure Rust, so no -dev packages) or windows-sys. The tray menu is drawn by hand with a built-in 5x7 bitmap font. The Linux release is a static musl binary.
  • Click-through overlays. On X11 it's an override-redirect ARGB window with an empty XFixes input region. On Windows it's a layered window via UpdateLayeredWindow.
  • Type-checking Win32 from Linux. Win32 code is gated with cfg(any(target_os = "windows", windows_check)), so RUSTFLAGS="--cfg windows_check" cargo check type-checks the Windows backend without a Windows toolchain.

Limitations: there's no native Wayland support yet, and X11 needs a compositor for transparency.

I'd love feedback on the code, especially the simulation and rendering split. Happy to answer questions about the animation.