asciirave
v0.2.1
Published
Music videos rendered live in your terminal, in characters, locked to every beat. Follows the song playing in your music app.
Downloads
519
Maintainers
Readme
asciirave
Music videos rendered live in your terminal. In characters, locked to every beat. No video files.
npx asciirave nowPlay a song in your music app and the matching film plays in your terminal, in sync with what you hear. Watch them in the browser too: asciirave.com
30 films so far: Tame Impala's Currents and Deadbeat, every track, plus the most-played songs. Stations: Tame Impala FM, Currents, Deadbeat.
Commands
| | |
|---|---|
| npx asciirave now | follow the song playing in your own music app (Apple Music on macOS, any MPRIS player on Linux, any app on Windows). On a song without a film yet, press r to request it |
| npx asciirave | pick a station and tune in: everyone on a channel sees the same frame at the same moment |
| npx asciirave play --film borderline --audio song.mp3 | play one film with your own audio file |
| npx asciirave local --media ~/Music | pick any song from your music folder and play it, radio style |
| npx asciirave request "<song>" | ask for a film (sign in with GitHub, no codes to type) |
| npx asciirave requests | see how your requests are doing |
Keys: space pause · ← → seek · + - nudge sync (now) · q quit. --mode blocks swaps characters for half-block pixels.
Sound and lyrics
asciirave hosts no audio and no lyrics.
- Sound comes from your own music app (
asciirave nowonly reads which track is playing and where), from a file you already have (--audio), or on asciirave.com from the official YouTube uploads. - Lyrics come from your own
.lrc(--lyrics, or~/.asciirave/lyrics/<film>.lrc), otherwise from LRCLIB (community-sourced timed lyrics), fetched by your machine at play time.--no-lyricsskips it.
How it works
A film is data: a timeline and a beat map (beats, downbeats, band energy). The film engine draws a tiny 160×90 world every frame and turns it into terminal characters, truecolor, sending only the cells that changed.
The engine is one WebAssembly module. Before it runs, the CLI checks its SHA-256 and an Ed25519 signature from the asciirave key, and that its only import is its own memory: it can compute cells and nothing else. No native dependencies. Node 22+ and a truecolor terminal (iTerm2, Kitty, WezTerm, Ghostty, Windows Terminal, recent Terminal.app...).
Credits
Made by @betterclever & claude. An unofficial fan project; songs belong to their artists.
Follow your music app (asciirave now)
| Where you listen | Works how |
|---|---|
| Apple Music (Mac) | built in |
| Spotify desktop (Mac) | built in, read through its AppleScript interface, only while it is already open |
| Linux: any player, including Spotify and browsers | built in, through playerctl |
| Windows: any app, including browsers | built in, through the system media controls |
| YouTube / YouTube Music in Chrome, Edge, Brave or Arc | run asciirave extension once, then load the folder it prints ("Load unpacked" in chrome://extensions) |
| YouTube tab, Mac, nothing to install | asciirave now --tab-titles: only the titles of YouTube tabs, no play/pause or position |
The extension is read-only. It can see the title, artist, position and length of what is playing on music.youtube.com and youtube.com, and passes them to the asciirave process on your own computer (127.0.0.1, with a private token). It cannot read other tabs, cookies or pages, and nothing goes to any server. asciirave never asks you to turn on "Allow JavaScript from Apple Events", which would let other apps run code in your tabs.
When several sources are open, the one that is playing wins, and it stays until another starts. Press s to open a list of every player that is reporting something (▶ playing, ❚❚ paused, with the song on each) and pick one with the arrow keys or its number; a goes back to automatic. A picked player is followed even while another one is louder, and the footer says "pinned". If it goes away, asciirave falls back to whichever is playing.
A song that isn't in the library
asciirave now doesn't stop at "no film yet". It fetches the song's timed lyrics from LRCLIB (on your machine, never stored),
reads every line for what it is about (rain, night, phones, roads, the sea, love, time, ...), and cuts a film on the spot:
one picture per idea, the same picture each time a line comes back (changing colour as the song builds), scenes for the gaps,
and moving transitions between the parts. The footer says "cut from the lyrics", and r asks for a properly made film.
Beats. A film reacts to the music through a beat map (beats, bars, bass / mids / highs). For a song that isn't in the library
there is none, and a live stream can't be analysed, so the picture stays calm and the header shows no tempo. If asciirave
can find the song as a file, it analyses it on your machine (FFT, beat tracking; a few seconds, cached) and the film reacts
to the real beats: a file in ~/.asciirave/media, in the folder from --media, or a local (non-streamed) Apple Music track.
It needs ffmpeg (or afconvert on a Mac). --no-generic turns generic films off.
