DEV Community

Cover image for A Tiny Linux Desktop Companion — and Wayland and GTK Kept Getting in the Way
Mika Flowers
Mika Flowers

Posted on AI-assisted

A Tiny Linux Desktop Companion — and Wayland and GTK Kept Getting in the Way

The first version of Mochi was supposed to be simple... I wanted to draw a small character, put him on the desktop, let him walk around, react to clicks, and occasionally do something cute.

Instead, I ended up debugging window coordinates across XWayland, fighting GTK popovers on multiple monitors, and learning that “just move the window” is not a complete desktop architecture.

Mochi is a tiny Linux desktop companion I’ve been building with Python, GTK, Cairo, and a lot of hand-drawn pixel art. He walks around the desktop, reacts when you pick him up, falls asleep when you’ve been away, notices when you’re typing, and can even sit down with his own little laptop while you work.

The most interesting part of building him, though, hasn’t really been the animation system. It has been trying to make a tiny character behave like part of the desktop when the desktop was designed to keep normal applications from doing exactly that.


The idea started with Codex Pets

Mochi watching me code

One of my original inspirations was Codex Pets. I loved the idea of having a tiny character existing alongside your work instead of inside a traditional application window. There was something charming about a little companion simply being there while you coded.

I wanted to experiment with that same feeling on Linux. At first, I thought Mochi would basically be my own Linux version of that idea, but the project started developing its own identity pretty quickly.

I didn’t want Mochi to simply be an animation sitting on top of the desktop. I wanted him to feel aware of the environment without becoming intrusive. If I’ve been typing for a while, he might sit down with his own laptop. If I’m watching something, maybe he watches too. If I walk away from the computer, eventually he should fall asleep. If I pick him up and fling him across the screen, his body should react to the movement.

Most importantly, I don’t want Mochi to constantly demand attention. That eventually became one of the main ideas behind the project:

A desktop companion should feel present without becoming another notification system.

Codex Pets helped spark the original idea, but Mochi became my attempt to explore what that kind of companion could look like if it were designed specifically around the Linux desktop.


The problem with making a desktop companion

mochi on a terminal typing

Normal applications want a window. Mochi wants to behave more like a small part of the desktop itself.

He needs to stay above normal windows, remember his position, move freely around the screen, survive multiple-monitor setups, work with different display scales, respond to dragging, show menus beside himself, avoid stealing keyboard focus, and continue animating smoothly while all of this is happening.

From the user’s perspective, none of that sounds particularly unusual. You grab the character, move him somewhere, and expect him to stay there.

From the perspective of a modern Linux desktop, however, it is a collection of awkward edge cases.

Wayland intentionally gives applications far less control over global window positioning than X11 historically did. That is a good tradeoff for security and compositor consistency, but it makes desktop-pet behavior much more difficult.

GTK adds another layer of abstraction. It gives me a clean toolkit for rendering interfaces and handling input, but Mochi needs behavior that doesn’t always fit neatly into the traditional application-window model.

Building Mochi has therefore become a constant negotiation between what I want the character to do, what GTK exposes, and what GNOME and Wayland actually allow.


The current stack

Mochi is written primarily in Python, using GTK4/PyGObject for the application and interface layer and Cairo for rendering. My primary development environment is Fedora Workstation with GNOME, and the project currently makes use of Wayland, XWayland, X11 APIs, D-Bus, and a small optional GNOME Shell extension.

The character itself is made from hand-drawn PNG sprite frames rendered with nearest-neighbor scaling so the pixel art remains crisp. GTK handles things like menus, developer controls, speech bubbles, configuration, and interaction surfaces.

On paper, that sounds fairly straightforward.

Then I tried to make a tiny green blob move freely around a Wayland desktop.


Wayland made “move the window” complicated

On X11, applications historically had much more control over their own window positions. Wayland deliberately removes a lot of that control.

For most applications, that is exactly what you want. A normal application should not be able to arbitrarily move itself around your desktop.

Mochi, unfortunately, is not a normal application.

He needs to know where he is, move in response to dragging, remember where the user left him, and behave consistently when moving between different monitors.

On my current GNOME setup, Mochi deliberately runs the main buddy window through XWayland. GTK still handles the application itself, but I use X11 APIs when I need precise root-window positioning.

That means Mochi has to deal with several coordinate systems at once: GTK logical pixels, display scaling, X11 device pixels, monitor coordinates, and application coordinates.

There were several moments during development where moving Mochi ten pixels visually did not mean moving the actual window ten pixels.

Multi-monitor setups made this even more interesting. A position that looked completely correct on one monitor could suddenly become offset on another because scale factors and coordinate systems were not lining up the way I expected.

That was one of my first big lessons from this project: a desktop companion is not just a drawing problem.

It is a window-management problem.


Making him feel alive without making him annoying

This is probably the part of Mochi I care about most.

I don’t want him constantly demanding attention. The goal is ambient companionship.

Mochi should notice enough about what is happening to feel connected to the desktop, but most of the time he should simply exist quietly alongside the user.

He can react to things like sustained typing, media playback, long idle periods, returning after being away, file browsing, broad application categories, battery and charging events, and network changes.

Knowing that one of these events happened does not automatically mean Mochi reacts to it. The system uses cooldowns, probability checks, and other restrictions so that his behavior stays subtle.

Sometimes the correct reaction is nothing at all.

That silence is part of the design.


Privacy became part of the architecture

As soon as I started experimenting with context-aware behavior, I ran into an important question:

How much should a desktop companion actually know about what you’re doing?

My answer is: as little as possible.

Mochi does not need to know what you are typing. He only needs to know that sustained typing is happening.

Because of that, the GNOME integration is intentionally built around reduced semantic signals. Instead of sending something like “the user pressed the A key,” Mochi receives something closer to “typing activity happened.”

The same idea applies elsewhere. Application detection tries to reduce what it sees to broad categories such as editor, terminal, browser, media, pixel art, or unknown instead of passing arbitrary window contents around.

For file activity, Mochi does not need to know the filename. He only needs to know that file browsing is happening.

There is one intentionally narrow exception for detecting focused YouTube playback, but even there the Shell side reduces what it sees down to a simple boolean signal before handing anything to Mochi.

I want the privacy boundary to be understandable.

A cute desktop companion should not require surveillance of your desktop.


The art is intentionally handmade

Everything you see on Mochi is hand-drawn pixel art.

I use Pixelorama for the animation work, and every animation is made from authored frames rather than procedural deformation or generated artwork.

That includes his idle breathing, blinking, walking, squishing, pickup animation, drag poses, sleeping, waking, typing, watching, hearts, drops, and other small emotes.

A surprising amount of personality comes from tiny details. How long one frame is held matters. How far his body stretches during a drag matters. Whether an animation returns directly to idle or flows naturally into another state matters.

Even something that is technically smooth can still feel emotionally stiff.

Working on Mochi has made me realize that character animation has a lot in common with interface design.

A few milliseconds can completely change how an interaction feels.


Timing is more complicated than FPS

Mochi has a manifest that describes animation data such as frame files, frame rate, whether an animation loops, and what should happen after it finishes.

But some animation timing is deliberately overridden at runtime.

The idle animation, for example, uses individual durations for different frames instead of playing every frame at a uniform FPS. That allows the breathing animation to linger in certain poses and move more quickly through others.

Blinking gets modified dynamically too. Occasionally Mochi performs a double blink by inserting a short pause and replaying part of the blink animation.

Walking is even stranger. Instead of letting the walk cycle run completely independently, the animation can be synchronized with actual movement progress.

That prevents the visual walk cycle from feeling disconnected from where Mochi is physically moving on the desktop.

Small systems like this have made me appreciate how much perceived polish comes from synchronization rather than just higher frame rates.


Building Mochi changed how I learn programming

This project has probably taught me more than another isolated tutorial would have.

When something breaks, I have a reason to understand it.

When I needed reliable menus, I learned more about GTK window behavior. When dragging felt wrong, I ended up reading about X11 pointer coordinates and compositor ownership. When ambient reactions became messy, I learned why state machines and event ownership matter.

When several timers started interacting with one another, I learned why it is important to know exactly which subsystem owns each callback.

And when I wanted Mochi to understand some basic desktop context, I ended up learning more about D-Bus, GNOME Shell extensions, Mutter, accessibility APIs, and the boundaries between a desktop environment and the applications running inside it.

Because every lesson immediately turns into something I can see Mochi doing, the learning sticks.

That is probably my favorite part of this project.

I am not learning these systems because a tutorial told me they were important.

I am learning them because a tiny green blob broke something and forced me to figure out why.


What I’m working on now

Mochi is still in active development. Right now I’m continuing to polish animations and interactions, improve multi-monitor reliability, expand ambient reactions, refine the GNOME integration, improve developer and debugging tools, work on packaging, and test the project across more Linux hardware.

I’ve also started building a separate Mochi Master Notebook that documents how all of the internal systems fit together.

One of my goals is for the project to remain understandable as it grows instead of slowly turning into a pile of code I’m afraid to touch six months from now.


First alpha

I’m currently aiming for Mochi’s first public alpha on September 21, 2026.

Fedora and GNOME are the primary development targets right now. In the future, I would like to explore what supporting additional Linux desktop environments looks like without compromising the experience that makes Mochi feel like Mochi.

There will almost certainly be bugs.

Actually, considering the amount of strange desktop behavior I have already encountered, there will definitely be bugs.

That is part of the fun.


Why I’m sharing this

I’m still learning, and that is exactly why I wanted to write about Mochi.

It is easy to look at an open-source project after it has matured and only see the finished architecture. You do not see the context menu that only worked after moving the character onto another monitor. You do not see the animation that accidentally loops five times. You do not see the tiny interaction that causes three different timers to fight each other.

You definitely do not see someone staring at a green blob at 2 AM wondering why GTK thinks it is somewhere else.

But that messy middle is where I have learned the most.

Mochi started as an attempt to build my own Linux desktop companion, inspired in part by the simple charm of Codex Pets. It has turned into my excuse to explore a huge portion of the Linux desktop stack, and somewhere along the way it became something that feels completely like its own project.

I think that is what makes it one of my favorite things I have ever worked on. 🌱


Project

Mochi is open source and still under active development.

GitHub: github.com/miflow13/mochi-desktop

If you’re into Linux, Fedora, GNOME, GTK, Python, pixel art, or desktop development, I’d love to hear what you would want from a little desktop companion like Mochi — especially the weird edge cases I probably haven’t discovered yet.

Top comments (0)