Googly Eyes
A pair of googly eyes for the Omarchy bar. They follow your pointer wherever it goes on screen, blink now and then, and occasionally wink at you.

A Wayland port of Sindre Sorhus's macOS menu bar app, which is the better-looking original and where the idea comes from.
Install
omarchy plugin add https://github.com/perminder-klair/omarchy-googly-eyes.git --enable
Or, to work on it locally, point the plugin directory at a checkout:
ln -s ~/Projects/googly-eyes-omarchy ~/.config/omarchy/plugins/co.klair.googly-eyes
omarchy plugin enable co.klair.googly-eyes right
Settings
Set these on the widget's entry under bar.layout in
~/.config/omarchy/shell.json:
{ "id": "co.klair.googly-eyes", "eyes": 2, "size": 0, "wobble": true }
| Key | Default | What it does |
|---|---|---|
eyes |
2 |
How many eyes, 1–6. One is a cyclops. |
size |
0 |
Eye diameter in pixels. 0 sizes them off the bar height. |
blink |
true |
Blink on a random 3–9 second cadence, and on click. |
winks |
true |
Close one eye instead of both, about one blink in five. |
wobble |
true |
Spring physics, so the pupils overshoot and settle like loose plastic discs. Off gives a rigid mechanical follow. |
themeColors |
false |
Paint the eyes in the bar's foreground/background instead of white and black. |
activeHz |
60 |
Cursor polling rate while the pointer is moving, 1–144. |
allowMultiple is on, so you can put a pair on the left of the bar and another
on the right and have them all watch you at once.
Blinking when you click
The eyes blink when you click them, but blinking on every click anywhere — the way the macOS app does — needs the compositor's help, because a Wayland client is not told about clicks that land on someone else's window.
Hyprland will do it with a non-consuming bind, which fires the dispatcher and
still passes the click through to whatever you actually clicked on. Add this to
~/.config/hypr/bindings.lua:
o.bind("mouse:272", "Blink the googly eyes", "omarchy-shell -q googly-eyes blink", { non_consuming = true })
Add mouse:273 and mouse:274 for right and middle click if you want those
too. Note what this costs: it spawns a short-lived process on every single
click you make, anywhere. It is a few milliseconds and you will not feel it,
but it is the reason this is opt-in rather than shipped on.
IPC
omarchy-shell googly-eyes blink # blink every eye
omarchy-shell googly-eyes wink # close one eye
omarchy-shell googly-eyes cursor # where the eyes think the pointer is
omarchy-shell googly-eyes status # watchers, poll interval, socket, cursor
How it works
The whole plugin is one question asked over and over: where is the pointer?
Wayland will not answer it. A client is told where the pointer is only while
the pointer is over that client's own surface, which is precisely the case
this plugin does not care about — eyes that only look at you when you are
already looking at them are not much of a trick. So the question goes to the
compositor instead, over Hyprland's control socket, which answers cursorpos
in the same logical layout coordinates Quickshell reports monitor positions in.
No scale maths, and it works across monitors: put the eyes on one screen, move
the pointer to another, and they follow it there.
It talks to the socket directly rather than shelling out to hyprctl cursorpos.
Measured on this machine, a socket round trip is about 0.06ms against about 4ms
to fork a process — the difference between 0.3% of one core at 60Hz and a fifth
of it.
Two details that are not obvious:
- The socket is closed from this side the instant the reply lands. Hyprland
answers one command and hangs up, and if you let its FIN arrive first, Qt
raises
PeerClosedError— a warning per poll, sixty lines a second into the shell's log. Closing first is the whole fix. - Polling drops through three tiers. A still pointer is the common case on a laptop, and a timer firing sixty times a second keeps the CPU out of its deeper idle states even when each poll costs almost nothing. Moving gets the configured rate; two still seconds drops to 20Hz, which picks movement back up within 50ms; a still minute drops to 2Hz. The poller stops entirely when no eyes are on screen.
Each eye works out its own centre in layout coordinates and aims from there, rather than the widget aiming once for all of them. That is what makes a row of eyes look right — the left eye and the right eye disagree slightly about where you are, the way two real eyes do.
Requirements
Omarchy 4 (Quickshell shell plugins) and Hyprland. Nothing else — no daemon, no input-device access, no extra dependencies.
Without Hyprland the eyes still render, they just stare straight ahead.
Tests
node tests/model.mjs
Covers the reply parsing, the aiming maths, the polling tiers, and the setting clamps. The QML is left to the compositor.
License
MIT. Googly Eyes for macOS is Sindre Sorhus's; this is an independent reimplementation for a different desktop, not affiliated with it.