👨🏻‍🔬 Connect With Me

  • 🎯 Seeking Roles: Research or industry roles where I can leverage my multi-disciplinary toolkit, combining low-level systems logic, machine learning workflows, and modern web architecture, to solve real-world problems in engineering, finance, and adjacent fields.
  • 📝 CV / Resume: View my Resume
  • 📍 Location: Bologna, Italy
  • 📱 Phone: (+39) 366 4207296
  • 📧 Email: giovannigravili112@gmail.com
  • 🔗 LinkedIn: linkedin.com/in/giovanni-gravili
  • 🐙 GitHub: github.com/ghovax

Other Projects

🐱 Bringing Desktop Mascots to macOS with Swift

Desktop mascots have a special place in computing history, small, animated characters living on your screen, offering a bit of company during long coding sessions. The most famous is Neko, the little cat that chases your mouse cursor, originally written for the PC-9801 in 1989. For a recent project, I wanted to bring this concept to modern macOS, creating not just a faithful recreation of Neko but also a second mascot that explores physics-based animation. The result is two open-source Swift applications: xNeko, a modern implementation of the classic behavior with multiple character skins, and xMascot, a swinging pendant simulation that reacts to gravity and momentum. The code is available on GitHub here for xNeko and here for xMascot. Both projects contain releases, but because they have not been compiled in a while, the certificates have expired and the releases cannot be run; they need to be recompiled to function correctly.

Window Management & Rendering

The biggest trick in a desktop mascot isn’t the animation itself; it’s the window management. To make a character look like it’s living on your wallpaper, you have to strip away everything that makes a window a window. Building desktop overlays on macOS sits in an interesting intersection of technologies. You need the low-level control of AppKit to manage the window’s behavior, but you want modern tools for rendering. I configured the window to be borderless, transparent, and floating. The key property here is ignoring mouse events, which allows clicks to pass right through the mascot to the windows behind it, ensuring the mascot acts as a companion rather than an obstruction. Even the rendering engine choice depended on the specific needs of each mascot: for xNeko, I used SwiftUI to handle the simple frame-based animation loop, while for xMascot, I needed the continuous physics of SpriteKit to simulate the pendulum motion with gravity and damping.

One specific challenge was ensuring pixel-perfect rendering for the retro sprites. Modern macOS rendering defaults to smooth interpolation, which blurs low-resolution pixel art. To solve this, I had to explicitly configure the interpolation settings, using .none in SwiftUI’s Image views and .nearest filtering in SpriteKit textures. This preserved the sharp, crisp edges of the original 32x32 sprites, maintaining the authentic 1990s aesthetic even on high-DPI Retina displays.

Animation Logic

While they share the same windowing tricks, the two mascots use fundamentally different logical approaches to feel “alive.” xNeko’s behavior is driven by a finite state machine with about 18 states. It’s not generic AI; it’s a specific set of rules that evoke personality. Every frame, the app calculates the vector from the cat to the mouse cursor. If the distance is large, it picks a movement state based on the angle. If it catches the mouse, it enters an “idle” sequence, sitting, scratching an ear, yawning, and finally falling asleep. The cat doesn’t teleport; it moves a maximum of 13 pixels per frame toward the target. This simple logic of moving toward a target, stopping if close, and cycling animations if idle is all it takes to make the character feel responsive.

Under the hood, this movement logic relies heavily on custom Swift extensions to simplify the vector mathematics. I extended CGVector to conform to AdditiveArithmetic, allowing standard operators like + and * to work directly on vector types. This simplified the displacement calculations significantly; instead of manually managing dx and dy components, the code can express movement as natural vector math position += displacement * speed. This cleaner syntax reduced the likelihood of coordinate errors and made the complex state transition logic, which determines the cat’s facing direction based on the velocity vector, much easier to reason about and debug.

xMascot, on the other hand, relies on simulation rather than states. The mascot hangs from a chain modeled by the standard pendulum equation:

α=gLsin(θ) \alpha = -\frac{g}{L} \sin(\theta)

where angular acceleration depends on gravity and the current angle. I apply a damping factor every frame to simulate air resistance, causing the motion to decay naturally over time. To prevent it from becoming a static image, a background timer occasionally gives the mascot a small “push” or impulse, starting a swing. This makes it feel like the character is fidgeting or playing, reacting to a simulated physical world rather than just following a script.

For this physics simulation, efficiency was a key concern since the mascot runs continuously in the background. I implemented an optimization where the physics engine enters a “sleep” state when the mascot’s velocity drops below a negligible threshold. This prevents the CPU from wasting cycles solving differential equations for a stationary pendulum. Additionally, the chain’s motion utilizes a non-linear smoothing factor, where each link’s damping is scaled by its distance from the pivot, creating a subtle “wave” effect that makes the chain appear flexible rather than rigid. Projects like this might seem trivial compared to “serious” software, but they are great exercises in system interaction.