Recently I decided to dive down the Hyprland rabbit hole. Rather than using an existing Hyprland config, I decided to build up my own environment from scratch. For simplicity, I opted to use Waybar to build a minimal-but-useful top bar. While Waybar has loads of great modules to choose from, I could not find a CPU graph module. So, I hacked one together myself, and it turned out pretty cool.

Waybar has great built-in modules for system monitoring, but they mostly (all?) show only the current state. I wanted something that would show a graph of the CPU usage over the last 60s. I’ve always admired how ‘docker’ uses braille characters to create a progress animation in a little 2x4 dot matrix. I decided to extend that clever trick and use a string of braille characters to draw a longer dot matrix; one large enough to render a small graph.

This is what I came up with:

Screenshot2
CPU history graph module

And here it is in the Waybar:

Screenshot1
CPU graph integrated into Waybar

The Implementation

You can find the full implementation in my dotfiles.

The graph script

The implementation is fairly straight forward. I wrote a python script that runs on a 1 second tick from Waybar. On every run, the script:

  1. Reads current CPU usage
  2. Maps usage to a 0-4 dot level
  3. Saves the reading to a history file
  4. Reads the history and maps the entries to braille characters

Since each braille character is a 2x4 dot pattern, each character must represent two entries in the history file (or 2 seconds of history). To keep things simple, I scan the history in groups of two, and look up the braille character from a dictionary of two-value tuples:

BRAILLE_PATTERNS = {
    (0, 0): '⠀', (1, 0): '⡀', (2, 0): '⡄', (3, 0): '⡆', (4, 0): '⡇',
    (0, 1): '⢀', (1, 1): '⣀', (2, 1): '⣄', (3, 1): '⣆', (4, 1): '⣇',
    (0, 2): '⢠', (1, 2): '⣠', (2, 2): '⣤', (3, 2): '⣦', (4, 2): '⣧',
    (0, 3): '⢰', (1, 3): '⣰', (2, 3): '⣴', (3, 3): '⣶', (4, 3): '⣷',
    (0, 4): '⢸', (1, 4): '⣸', (2, 4): '⣼', (3, 4): '⣾', (4, 4): '⣿',
}

One point to mention, I didn’t do a simple uniform quantization from CPU usage to the dot level. In practice the difference between 0.1% load and 5% is significant, and I want the graph to show that. A uniform quantization would give a mapping of (0-20%, 20%-40%, 40%-60%, 60%-80%, 80%-100%). Any CPU value between 0%-20% wouldn’t even show on the graph… that’s useless to me. So I decided to choose different quantization levels to emphasize the usage ranges I care about:

  • 0-1%: No dots (idle)
  • 1-12.5%: 1 dot (light usage)
  • 12.5-25%: 2 dots (low usage)
  • 25-50%: 3 dots (moderate usage)
  • 50%+: 4 dots (high usage)

Waybar Integration

I then created a custom Waybar module to render the graph in my top bar. I added a toggle mode to hide the graph, and a tool-tip to show the instantaneous per-core CPU usage:

  "custom/cpuhistory": {
    "exec": "~/.config/waybar/scripts/cpu_history.py -d 60",
    "format": "  {}",
    "interval": 1,
    "on-click": "~/.config/waybar/scripts/cpu_history.py toggle",
    "return-type": "json"
  },

Closing Thoughts

This was a quick and dirty solution. There’s a few things I might’ve done differently, if this was more than just a quick script for some cosmetic eye candy; e.g. making parameters easier to tweak, or re-thinking the dot-levels, etc. That said, I’m more excited by how little effort it took to build something genuinely useful, and I don’t want to ruin that balance of utility vs effort 😉