# wob: a tiny Wayland overlay bar you drive from a named pipe

> wob is a small C program that shows a value in an overlay bar for Wayland compositors supporting wlr-layer-shell, and it takes its input from standard input, which in practice means a named pipe you write to. It is deliberately unopinionated: no volume control, no brightness control, no keybindings, just a bar and a pipe.

**francma/wob** — Lightweight overlay volume, brightness (or anything) bar for Wayland 

- Repository: https://github.com/francma/wob
- Stars: 1,164 · Forks: 55
- Language: C
- License: ISC
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/francma-wob

## A bar that displays a number and nothing else

wob is described in the README as a lightweight overlay volume, brightness (or anything) bar for Wayland compositors with support for the wlr_layer_shell_unstable_v1 protocol. The primary language of the repository is C, the licence is ISC, and the topics are sway, swaywm and wayland.

The scope is genuinely narrow, and reading the usage instructions is the fastest way to understand it. Launch wob in a terminal, enter a value, which is a positive integer, and press return. That is the whole interaction model in the general case, and there is no second step, no configuration screen, no keybinding table. wob does not control your volume, it does not change your backlight, and it does not know what the number means. It shows a bar whose fill represents a number you gave it.

That is the design, and it is a deliberate one. The name in the parenthesis, or anything, is doing the work. Because the program has no opinion about what it is displaying, the same binary can be your volume indicator, your keyboard backlight indicator, your microphone level, or a progress indicator for a download, without any configuration change. The only requirement is that something writes a number to it.

The consequence is that wob is a building block rather than a finished tool. On its own it is a number display. It becomes useful when it is connected to the thing that produces the numbers, and the README's own framing is that of a component: adapt this use-case to your workflow, which it lists as scripts, callbacks, or keybindings handled by the window manager. The window manager mention is the important one on Wayland, where a compositor such as sway can bind a key to a command that changes the volume and then tells wob about it.

The alternatives section in the README makes the trade-off explicit against a set of competitors that are more opinionated. avizo is described as icons, volume and brightness. SwayOSD is icons, volume, brightness and keyboard state such as caps lock and num lock. A notification daemon with a custom script is another route. And xob, the X11 overlay bar that inspired wob, is noted as X11 only. So wob sits at the minimal end of that list on purpose.

## The named pipe pattern, and why it is the interface

The general case in the README is the mechanism worth understanding, and it takes three commands.

First you create a named pipe on the filesystem, for example at /tmp/wobpipe:

```bash
mkfifo /tmp/wobpipe
```

Then you connect that pipe to the standard input of a wob instance:

```bash
tail -f /tmp/wobpipe | wob
```

And then, whenever the value changes, whatever is managing it writes the new value into the pipe:

```bash
echo 43 > /tmp/wobpipe
```

So the design is that a long-lived wob process is fed by a reader on the pipe, and any writer can push a value into it. The tail -f is what keeps the pipe open and pumps whatever arrives into wob's standard input.

This is an unusually clean separation and it explains why the program is small. There is no socket protocol, no D-Bus interface, no IPC negotiation, no client library. The process boundary is a byte stream on a file descriptor, and the value is a line of text. Anything that can write to a file descriptor can drive this, which means a shell script, a keybinding handler, a media key daemon, a MIDI controller mapping, a status line script, or a shell function can all be the source.

It also explains the performance characteristic, without needing to guess at a benchmark. Each write is a handful of bytes, and the bar is a layer-shell surface the compositor redraws. There is no polling loop over a long list of things to watch, and no periodic sync. The cost of showing a value is proportional to the number of times the value changes, and each change is a single small write.

The trade is that the ordering of responsibility is inverted from a typical OSD. A tool like SwayOSD owns the volume control and tells you about it. wob owns nothing, and the thing that changes the volume tells wob. That is more work for the user to set up, and it is also what makes wob small, portable across compositors, and usable for things nobody thought of when it was written.

## Building it with meson, and the optional dependencies

wob builds with meson and ninja, and the README gives the full sequence:

```bash
git clone git@github.com:francma/wob.git
cd wob
meson build
ninja -C build
sudo ninja -C build install
```

The dependency list is short and the annotations matter, because three of the entries are optional and one of those is a security feature rather than a convenience.

The required dependencies are wayland, inih, and wayland-protocols, with the last two marked as compile-time dependencies. inih is a small INI parser, and its presence tells you where the configuration handling comes from, since the README points to wob.ini.5.scd for styling and positioning options, so configuration is a file of key and value pairs parsed at startup rather than a runtime protocol.

meson is marked compile-time as well, since it drives the build. scdoc is optional and is for the man page, and the repository contains wob.1.scd and wob.ini.5.scd as the scdoc sources for the two manual pages. cmocka is optional and is for the tests, and the repository has a test/ directory plus a GitHub Actions workflow badge for the test job.

The dependency worth pausing on is libseccomp, marked optional and described as Linux kernel syscall filtering. A program that only reads a line of text and draws a bar needs very few system calls, so the natural reading is that this is a hardening option: you can confine the process's syscall surface so that a compromise of the process is less useful. It is a defensible choice for a program that is run repeatedly on keypress and does nothing but display what it is told.

The build system also has meson_options.txt, which is how the optional features are usually switched on and off, and the tree carries a.clang-format and a.clang-tidy, so the C code is held to a specific formatting and linting standard, plus a.editorconfig for the rest of the files. The protocols/ directory holds the Wayland protocol definitions the project generates against, which is where the layer shell support lives.

## Styling through an INI file, and the systemd integration

Two pieces of the documentation are worth knowing about before you run it.

The first is the configuration. The README points to wob.ini.5.scd, the scdoc source of the manual page for the configuration file, as the reference for styling and positioning options. Given the inih dependency, the file is INI format, with sections and keys, and it is read by the process at startup. Since the program is meant to be launched and relaunched, or at least reconfigured, the question of whether a running wob picks up changes is one the man page would answer and the README does not.

For a Wayland overlay, the positioning options are the substance rather than the decoration. A bar drawn in the wrong place overlaps whatever your compositor already draws, and the layer shell protocol gives a program a defined surface type and an output to anchor to. Getting the bar to appear where you want it, at a size that does not obscure the content under it, is the part of the configuration that actually gets fiddled with, and the fact that the project ships a dedicated manual page for it says it is configurable in more than a couple of ways.

The second is the systemd integration, with a README under etc/systemd referenced from the main README. A daemon unit for a program like this solves a real problem: the pipe and the tail -f process have to be running for the bar to appear at all, and in a desktop session managed by systemd that is a user service with a lifecycle, a restart policy, and a defined order relative to the compositor starting. A user unit that starts wob with its pipe attached is the natural way to make the overlay appear on keypress without the user wiring anything by hand.

The repository also has a contrib/ directory with a README pointing to example setups, which is where you would look for the callback and keybinding examples that make the whole thing usable. The etc/ directory and the contrib/ directory together cover the two things a user needs that the program itself does not provide, the deployment and the wiring.

## The limits: layer shell, single value, no policy

There are three constraints that decide whether wob is usable for you, and none of them are bugs.

The first is the protocol requirement. The bar is drawn using wlr_layer_shell_unstable_v1, which is the protocol for overlay surfaces in wlroots-based compositors. A compositor that does not implement it will not show the bar at all, and the README names the protocol explicitly rather than claiming universal Wayland support. Sway is in the repository topics, along with swaywm, and the protocol is used by a family of compositors built on wlroots. A compositor outside that family, or one that has not implemented the layer shell, is out of scope. The name unstable in the protocol name is also a real caveat, since unstable protocols can change between compositor versions.

The second is the input model. The value is a positive integer, and the bar represents it as a proportion. That means the mapping from the number to the bar length is fixed by the program's own range, and the README does not document a configuration key to change that range or to set a maximum. If your volume steps from 0 to 100 but the program assumes a 0 to 100 range, it works, and if your scale is different, you are expected to scale it yourself before writing to the pipe. The upside of that is that the program's assumptions stay simple. The downside is that it assumes your callback knows what range to send.

The third is the absence of policy. wob does not decide when to show, how long to stay visible, or what a change means. It has no notion of a mute state or a low-battery warning, because those are decisions about the domain, and the project leaves them to the caller. That is what makes it small and what makes it require you to write the logic that a finished OSD would have shipped. The competitors listed in the README, avizo and SwayOSD, are the tools to reach for when you want that logic handled for you.

A related practical point: the pipe example puts the FIFO in /tmp, which is world-writable on a typical system. Anything running as another user on the same machine that can write to that path can make your bar display whatever it likes, and the value is written by whoever gets there first. For a single-user desktop this is a non-issue. On a shared machine it is worth using a path in your own home directory with the right permissions instead.

## wob against the other overlay bars

The README's alternatives list is the comparison, and the differences are mostly about who owns the logic.

avizo is described as icons, volume and brightness. SwayOSD is icons, volume, brightness and keyboard state, which means caps lock and num lock. Both are complete on-screen display programs: they know what they are showing, they have icons. wob knows none of that. It shows a bar and a number.

The difference in approach shows up in the dependencies. A finished OSD that displays volume has to know how to read the volume, or at least how to be told it, and if it reads it, it needs a PulseAudio or PipeWire client library. wob needs wayland and inih and nothing else, because the value arrives on standard input. The cost of that minimalism is that the integration work moves to you.

The third alternative in the list is a notification daemon plus a custom script, which is interesting because it uses the notification system itself as the display surface. That route gives you a styled notification with a title and body instead of a bar, and it fits into a desktop's existing notification flow. The difference in approach is that the notification is transient and text-oriented, whereas a layer shell surface can be a persistent bar with a fill level, and the bar is closer to what a volume indicator looks like.

xob is credited as the X11-only inspiration for wob. That tells you the lineage: wob is the Wayland equivalent of a small X11 program that did the same job, and it inherited the minimal design while moving to the layer shell protocol.

So the choice comes down to a single question. If you want your keybindings to bring up a volume bar and nothing else, xob-style minimalism is wob and it is very small. If you want a program that handles volume, brightness and keyboard indicators with icons, SwayOSD or avizo will save you the integration work. And if you want something that shows a value that has nothing to do with either, a progress bar or a level meter, wob is the only one of the four that can be told what to display.

## Conclusion

Adopt wob if you run a Wayland compositor that supports wlr-layer-shell and you want a minimal overlay bar that your own scripts or keybindings drive, with no daemon and no opinion about what the value means. Do not adopt it if you want a tool that manages volume and brightness itself, since wob only displays numbers, and check that your compositor implements the layer shell protocol before you start. Verify first that the pipe path in your setup is writable by the process that writes to it, that the bar's placement does not collide with the panels your compositor already draws, and that the release you install is signed, since releases are published with a specific OpenPGP key.

## FAQ

### What does wob actually control?

Nothing directly. It displays a value you give it, such as audio volume or backlight intensity, and the README describes it as an overlay volume, brightness (or anything) bar. You write the value into a named pipe and the program shows it as a bar.

### How do I build and install wob from source?

Clone the repository, then run `meson build`, `ninja -C build` and `sudo ninja -C build install`. The required dependencies are wayland, inih and wayland-protocols, and scdoc for the man page, libseccomp for syscall filtering and cmocka for the tests are optional.

### Which Wayland compositors support wob?

It needs support for the wlr_layer_shell_unstable_v1 protocol, which the README names explicitly. The repository topics are sway, swaywm and wayland, so it is aimed at wlroots-based compositors, and a compositor without layer shell support will not show the bar.

### How do I connect wob to my own scripts?

Create a named pipe with mkfifo, connect it to wob's standard input with a command like `tail -f /tmp/wobpipe | wob`, and then write values into the pipe, for example `echo 43 > /tmp/wobpipe`. The README points to the contrib directory for examples and to the etc/systemd directory for a systemd unit.

## Sources

- [francma/wob on GitHub](https://github.com/francma/wob)
- [Issues](https://github.com/francma/wob/issues)
- [License: ISC](https://github.com/francma/wob/blob/master/LICENSE)
- [README](https://github.com/francma/wob/blob/master/README.md)
- [Releases](https://github.com/francma/wob/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/francma-wob
