To listen to music while I work I used the browser, with a tab for each thing: one for lofi, one for a particular artist and one for the radio. Every switch pulled me out of the terminal, which is where I spend my day.

What bothered me most was something else, though: none of it felt like mine. It did not have my colours and it was not set up the way I like. I wanted to open it and feel at home with what I saw.

The closest thing I knew was gentle-stream, by Gentleman Programming. I liked its interface, but it generates music and I was looking for a player. The other piece was cliamp: I liked its interface too, and I was already listening to its radio.

That is how velo started: a player for the terminal that owes a lot to those two projects.

What it does

velo is a music player for the terminal. You point it at your folder, it indexes the library from your files' own tags, and you browse it by artist, album, track or genre.

A half-minute tour of velo: internet radio, the queue edited without stopping the music, the thirteen themes and the nine visualizers

This is a video in the original post.

  • Local library. The index lives in SQLite, refreshes in the background on every launch, and has fuzzy search across all of it.
  • Gapless playback. MP3, FLAC, M4A/AAC, OGG Vorbis, Opus and WAV, with no silence between one track and the next.
  • An editable queue. Append, reorder, remove and shuffle without stopping the music.
  • Internet radio. The Radio Browser directory, the cliamp channels, or any stream URL.
  • Subsonic servers. Navidrome, Airsonic or Gonic sync into the same index. Still in early access.
  • What you expect from a serious player. ReplayGain with clipping protection, bit-perfect mode, output device selection and the system's media keys.
  • Cover art, nine visualizers and thirteen themes. Or your terminal's own colours.

The keys are Vim's: j and k to move, / to search, enter to play. ? lists all of them.

The library's tracks view while a song plays: the now-playing block with its clock and spectrum, and the list of tracks below

Turn on the transparent background and velo lets your terminal's own show through. This is it on my desktop, with a lofi channel playing:

velo in a translucent terminal window over a desktop wallpaper of a house covered in plants, the spectrum moving while a lofi radio channel plays

This is a video in the original post.

Favourites, playlists and history are kept in TOML files you can open and edit. And there is no telemetry: velo only contacts what you point it at, plus one request a day to GitHub to learn whether a newer version exists, which one line of configuration turns off.

What already existed

There are many terminal players and several are very good. The truth is I did not try most of them in depth. I looked at them as references while I designed velo, to see what each one solved and how.

One decision came out of that. Some of the best known, like ncmpcpp and rmpc, are MPD clients: before you hear a song you have to install, configure and keep a separate server running.

I wanted the opposite: one binary. You install it, tell it where your music is, and it plays. No daemon, no server and no account.

How it works inside

The audio callback only copies memory. The operating system asks velo for samples every few milliseconds, and if they arrive late you hear a click. So the expensive work (decoding, resampling, applying ReplayGain) happens on another thread, one buffer ahead. The two threads talk through a lock-free ring buffer. The callback measures how many samples are ready, copies them and fills the rest with silence. It does not allocate, take a lock or write a log.

Gapless means gapless. The next track is decoded before the current one ends and joined to it with no silence inserted. Lossy encoders add padding at the start and end of every file, and velo trims it at the seam. On a live album or a concept album, you can hear the difference.

One binary, for real. Every dependency had to pass that rule. SQLite is compiled in. Symphonia, the library that decodes the other formats, has no Opus decoder, so libopus is built statically. For media keys on Linux I chose a pure Rust D-Bus implementation, because the default one required a system library to be installed.

Remote servers go through a cache. A Subsonic track is downloaded once, untranscoded, and played from disk. That way seeking, gapless playback and ReplayGain work exactly as they do for a local file.

A radio channel while it plays: the live row with its listener count, and the channel's songs below

How I built it

The repository does not hide it, so I will say it myself: velo went from its first commit to version 1.0.0 in six days, with 479 commits and 75 published versions. I did not write those 70 thousand lines of Rust by hand. AI agents wrote them, with the same system I described in my AI dev stack.

What I did was direct. Every feature started as a document with numbered requirements that quote my own words, and every task was closed against those requirements. There are 21 of those documents in the repository's odd/tasks folder, in plain sight.

The code is not accepted on faith either. There are more than 1,200 tests, and a single script runs the formatter, the linter with warnings treated as errors, and every test. It is the same one continuous integration runs.

Not everything went to plan. velo was my first time with many of these technologies and with nearly every audio concept. Gapless, ReplayGain and bit-perfect were the most technical terms for me, and I did not fully understand what they implied. So when the agent proposed a scope I accepted it, and on top of that I asked for Subsonic servers, which I hardly use. It is in the project's first document, in my own words. Later there were moments when I did not know enough to question it, and I simply let it continue.

velo ended up bigger than I needed. That was not a bad thing: those features left the application more complete. But it made one thing clear to me: when you do not understand the subject, you stop directing and start approving.

Try it

velo runs on macOS and Linux. With Homebrew:

brew install alvaldes/tap/velo 

Or with the install script, which verifies the checksum and does not ask for sudo:

curl -fsSL https://alvaldes.github.io/velo/install.sh | sh 

Then run velo, press o and type the path to your music folder. On the site there is a player you can press play on before installing anything.

To see cover art you need a terminal that speaks the kitty, sixel or iTerm2 protocol. Windows is untested.

It is open source, under the MIT license. If something does not work or you have an idea for an improvement, open an issue in the repository.