After seeing pictures of people running desktop audio visualizers on Reddit, I started to think if it is possible to replicate the effect on my i3-gaps setup running on Windows Subsystem for Linux (WSL).

If you are interested in the basic idea of running i3 on WSL, you can check out this post, which describes the basic setup.

Okay, now that you know how to do the basic setup, we can now discuss the details.

Running the Visualizer on the Desktop

i3 does not support the EWMH hint _NET_WM_WINDOW_TYPE_DESKTOP, which tells the window manager to render the window on the desktop, behind all other windows. This makes it difficult to display things like visualizers on the desktop.

Fortunately, the authors of GLava managed to find a solution involving unmanaged windows (override_redirect) and XLowerWindow. Unfortunately, GLava does not run very well on WSL, requiring software rendering since the X11 server runs on Windows and is being treated as remote. On my machine, GLava runs, but consumes around 80% of the CPU on all cores. Clearly, another solution is needed.

Cava is a visualizer that runs in the terminal. The obvious solution here is to run Cava in a terminal emulator. I used urxvt as recommended by the author of Cava for its performance.

However, urxvt does not support running as a desktop window, and definitely does not support GLava’s hack to do it on i3. The same is true for any other terminal emulator that I am aware of. While it is possible to run a patched version with the hack builtin, I consider such a solution inelegant.

I recalled that urxvt supported embedding into another window, and thought that perhaps embedding urxvt into a window that is configured to run on the desktop would work. I tried out the idea it worked. And thus i3bgwin was born.

The command to run i3bgwin with a transparent rxvt running cava inside is:

$ i3bgwin urxvt -depth 32 -bg '[00]black' --color6 '[50]cyan' +sb -embed {windowid} -e cava

To run this automatically with i3, put the following in ~/.config/i3/config:

exec_always --no-startup-id killall cava; exec i3bgwin urxvt -depth 32 -bg '[00]black' --color6 '[50]cyan' +sb -embed {windowid} -e cava

Now, assuming you have Cava setup somehow, you should have Cava transparently over the desktop, and any i3-managed window should show up over it.

Sourcing the Windows Audio Output

Cava can only source audio from ALSA, PulseAudio or MPD via a FIFO (a named pipe). Clearly, ALSA is not possible.

It is possible to run the PulseAudio server on Windows and let Cava source audio from that, but it forces you to play music from inside WSL. In a future post, I might explain more about how to set up PulseAudio this way.

The last option is MPD. You can definitely run MPD on WSL and output to a named pipe, but it still forces you to play music from inside WSL.

Taking a closer look at the MPD option, I found that Cava simply expects raw signed 16-bit PCM audio sampled at 44100 Hz in the named pipe. So theoretically, it is possible to pipe in audio from any source, as long as it is in the correct format… even if the audio comes from Windows.

I found that fmedia supports capturing audio via WASAPI loopback, which is used to record the sound output. While other recording tools may work, the ones I tried could only record sound from input devices. You can use the hidden “Stereo Mix” input device on Windows to record the sound, but this makes Windows 10 display a microphone icon, warning that your microphone is being used. This makes it impossible to tell when your actual microphone is being used, defeating the privacy feature.

The following fmedia command will record your audio output to stdout:

fmedia.exe --notui --record --dev-loopback=1 --format=int16 --rate=44100 --channels=2 -o @stdout.wav

You might need to change --dev-loopback=1 to reflect your actual output device. The command to view the list of devices is fmedia.exe --list-dev.

Now, we simply need to feed this into a named pipe. We can easily achieve this with socat:

socat PIPE:<named pipe location> EXEC:"/mnt/c/path/to/fmedia.exe --notui --record --dev-loopback=1 --format=int16 --rate=44100 --channels=2 -o @stdout.wav"

You should replace <named pipe location> with the desired path for your named pipe, as well as /mnt/c/path/to/fmedia.exe. Then, you can configure cava to use this pipe:

[input]
method = fifo
source = <named pipe location>

Then, you can run cava to see your Windows audio output visualized. You may notice that there is a delay, and the movement is not smooth. This is because fmedia buffers. To fix this, open up fmedia.conf, find mod_conf "#file.stdout" and change the section to:

mod_conf "#file.stdout" {
    buffer_size 4k
}

At this point, everything should work.

To automatically start socat and fmedia when i3 starts, put this in ~/.config/i3/config:

exec_always --no-startup-id pkill -f -- '--record --dev-loopback=1'; exec socat PIPE:<named pipe location> EXEC:"/mnt/c/path/to/fmedia.exe --notui --record --dev-loopback=1 --format=int16 --rate=44100 --channels=2 -o @stdout.wav"

I hope you found this guide helpful in creating your dream workspace.