Documentation
Everything Deskbeat does, and how.
Install, the widgets, edit layout, settings and the file behind them, hotkeys, where lyrics come from, the architecture and the developer guide. Every statement here traces to the repository's own documents or its code.
Start here
What Deskbeat is
It is built as a light alternative to a Rainmeter setup. Widgets redraw only when something changes, and nothing is drawn at all while Spotify is silent or a window covers the desktop.
It is not a skin engine. There are no plugins, no scripts and no user-authored skins; every visual and behavioural parameter of the four widgets is a setting instead.
There is no Spotify login and no client ID: track details and controls come from Windows itself, so it works on free accounts.
What you get
Visualizer
A spectrum of Spotify's audio only, not your games or calls. Bars, mirrored bars or a wave.
Lyrics
Time-synced lyrics, followed word by word where the song has timed words and line by line where it does not. What is being sung either lights up at once or fills as it is sung. Word timing comes from a LyricsPlus server and is never guessed; line timing comes from LRCLIB. Cached on disk.
Player
Album art, title, artist, a progress bar you can click to seek, and previous, play/pause and next.
Clock
Day, time and date.
Start here
Download and install
Deskbeat-<version>.exe from the latest GitHub release and run it. There is no installer.- Download the exe. It is named for its version, so several downloads on one PC can be told apart.
- Run it. The exe is unsigned, so Windows SmartScreen will warn the first time: choose More info, then Run anyway.
- It lives in the notification area. Double-click its icon for settings, or right-click it for a small menu.
- Play something in the Spotify desktop app on the same PC. Playback sent to another device through Spotify Connect is not seen.
Checking a download
Each release carries a SHA256SUMS.txt and a build provenance attestation. The second proves the file was built by this repository's release workflow at that tag.
gh attestation verify Deskbeat-<version>.exe --repo akshit-bansal11/deskbeatStart here
Using it
Double-click the tray icon, or press Ctrl+Alt+S, for settings. The progress bar seeks where you click it, and the three buttons are previous, play/pause and next.
The tray menu
A right-click on the tray icon opens a small menu: the name and version, a link to the repository on GitHub, the Start with Windows switch and Quit. The app draws it itself, in its own three colours. Escape, or a click anywhere else, closes it. The settings panel has the switch and Quit too.
Where the widgets sit
| Layer | In config.toml | What it means |
|---|---|---|
| On the desktop | desktop | On the wallpaper, under every window. The default. |
| Normal window | normal | An ordinary window: whatever is clicked covers it. |
| Always on top | top | Above everything. |
By default the player, the lyrics and the visualizer hide while Spotify is not running, and drawing stops while a fullscreen app or game is in front. Both are settings. Only one copy of Deskbeat runs at a time: a second launch hands over to the first and exits.
Start here
Edit layout
Ctrl+Alt+E turns edit layout on and off. The clock and the player have no box, so every one of their elements is placed on its own.In edit layout the day, time and date, and the player's art, title, artist, each button, the progress bar and both times can each be dragged anywhere; the one under the pointer is outlined. Nothing else is drawn in edit layout: no frames and no labels.
Every element moves alone. Shift+click selects several, which then drag together. The space between elements does nothing, so nothing moves as a group unless you select it. The lyrics and the visualizer keep a frame, because it is what sizes them: drag the widget to move it.
The Player tab has two ready-made arrangements to start from, Art on the left and Centred stack, and the Clock tab can reset its three positions.
Start here
Settings
| Tab | What it covers |
|---|---|
| General | Four one-click looks (Glass, Minimal, Neon, Mono). The theme: font, accent, text and card colours, card opacity, corner and border, a shadow under text. The layer the widgets sit on, pausing behind fullscreen apps, hiding when Spotify is closed, global hotkeys. Hide all widgets, Start with Windows, and Quit. |
| Clock | Show it, put a card behind it, put its three rows back in place. A page each for the day, the time and the date: show it, pick what it shows, its position in screen pixels, and its font, size, weight, colour, opacity, letter spacing and capitals, which way it runs (across, down or up) and whether its letters stand upright or lie sideways. |
| Player | Show it, and its background: blurred album art, a card, or none. Two ready-made arrangements, Art on the left and Centred stack. Album art size and corner rounding. The progress bar's length, thickness, colour and unplayed part. The buttons' size and colour. The title, artist, time played and track length, each on a page of its own, positioned and styled like any other text, with the same Runs and Letters rows. Song name only, for the title. |
| Lyrics | Show them, a card behind them, whether what is being sung lights up at once or fills, word by word where available, their position and size, alignment, font, size, weight, lines above and below, line spacing, how much larger the current line is and how lines fade with distance. Colours for the current line, the current word and the other lines, an outline, a shadow, and timing: show earlier or later, and the scroll time. |
| Visualizer | Show it, a card behind it, its style (bars, mirrored bars or a wave), what it listens to (Spotify only, or everything), spanning the whole screen, turning it a quarter so the bars run down the screen, the number of bars, their width, the gap between them, roundness, dots for bars at rest, bass in the middle, and flipping left to right or upside down. Its colour: the accent, one colour or a gradient. Its motion: sensitivity, rise, fall, frames per second, treble boost and pitch range. |
Each widget also has a placement: the screen corner or edge it is measured from, so a layout survives a resolution change, its width and height, and its opacity.
Every colour setting has a picker: the last chip in its row opens a colour square, a hue bar and a hex field you can type into. A one-click look only touches appearance, never positions.
Text that runs down or up
Any text of the clock and the player can run across, down or up, with its letters upright or lying sideways. So the day can be a column of upright letters, or the title can read up the side of the album art. In the panel these are the Runs row (Across, Down, Up) and the Letters row (Upright, Sideways); in the file, direction and letters.
Fonts
To use a font without installing it in Windows, put its .ttf or .otf file in %APPDATA%\deskbeat\fonts and restart the app. The clock's day and time default to Anurati and Quicksand, the pairing the Mond Rainmeter skin uses; they are not shipped with the app, and the theme font is used until they are in that folder.
Start here
config.toml
%APPDATA%\deskbeat\config.tomlLoading lays the file over the default config table by table, so a partial file keeps every default it does not mention, and unknown keys are ignored. Keys that were renamed are carried over, and every number is clamped, so a hand-edited or older file cannot put the app in a state the settings panel could not.
The file has six tables: [general], [theme], [clock], [player], [lyrics] and [visualizer]. Colours are #RRGGBB or #RRGGBBAA; a text or bar colour can also be "text" or "accent". These are the keys most worth knowing; the schema has every one, with its default.
| Key | Default | What it does |
|---|---|---|
| general.layer | "desktop" | Where the widgets sit among other windows: "desktop", "normal" or "top". |
| general.pause_on_fullscreen | true | Stop drawing while a fullscreen app or game is in front. |
| general.hide_without_spotify | true | Hide the player, lyrics and visualizer while Spotify is not running. |
| general.hotkeys | true | The global hotkeys. |
| theme.font | "Segoe UI Variable Display" | The font every text uses unless it names its own. |
| theme.accent | "auto" | "auto" takes the colour from the album art; otherwise a hex colour. |
| clock.time_format | "%H:%M" | A hand-written clock format. day_format and date_format work the same way. |
| clock.day.size | 34.0 | Every element is a table of its own: x, y, align, font, size, weight, color, opacity, spacing, uppercase, direction, letters. |
| clock.day.direction | "horizontal" | Which way a text of the clock or the player runs: "horizontal", "down" (top to bottom) or "up" (bottom to top). |
| clock.day.letters | "upright" | "upright", or "sideways": turned a quarter, lying along the line, whichever way it runs. |
| player.background | "art-blur" | "art-blur", "card" or "none". |
| lyrics.mode | "complete" | "complete" lights up what is being sung at once; "progress" fills it from its start to its end. With lyrics.word_sync that is each word, where the song has timed words, and the line otherwise. |
| lyrics.offset_ms | 0 | Positive shows lyrics earlier. Clamped to ten seconds either way. |
| lyrics.word_sync | true | Ask the servers below for the time each word is sung. |
| lyrics.word_servers | two LyricsPlus mirrors | The servers asked for word timing, tried in order. Yours to edit or empty. |
| visualizer.style | "bars" | "bars", "mirror" or "wave". |
| visualizer.source | "spotify" | "spotify" listens to Spotify alone; "system" to everything the PC plays. |
| visualizer.bars | 120 | Between 4 and 512. |
| visualizer.full_width | true | Span the whole width of the screen, ignoring the frame's width. |
What the file takes that the panel does not
Any installed font, and a hand-written clock format such as %H:%M, %l:%M %p or %A, %e %B. Anything that is not one of these specifiers is left as written.
| Specifier | Shows |
|---|---|
| %H %k | Hour, 00-23 or 0-23 |
| %I %l | Hour, 01-12 or 1-12 |
| %M %S | Minute, second |
| %p | AM or PM |
| %A %a | Weekday, long or short |
| %B %b | Month name, long or short |
| %d %e | Day of the month, 01-31 or 1-31 |
| %m | Month, 01-12 |
| %Y %y | Year, four digits or two |
| %% | A percent sign |
Start here
Hotkeys
| Hotkey | Does |
|---|---|
| Ctrl+Alt+S | Open settings |
| Ctrl+Alt+E | Edit layout: drag any element of the clock or player on its own, or drag the lyrics or visualizer to move them |
| Ctrl+Alt+H | Hide or show every widget |
| Ctrl+Alt+Up | Show lyrics 100 ms earlier |
| Ctrl+Alt+Down | Show lyrics 100 ms later |
A hotkey that another app has already taken is left to that app; Deskbeat writes a line to its log and carries on. The switch is Global hotkeys on the General tab, or hotkeys = false under [general].
Start here
If a widget stays empty
Deskbeat-1.0.1.exe --probe > probe.txtWith Spotify playing, this records for eight seconds what Windows reports about the track and how loud Spotify's audio is.
--probe-systemlistens to everything the PC plays instead of Spotify alone.--probe-lyrics "Title" "Artist"looks one track up, for word timing and on LRCLIB.
Errors are logged to %LOCALAPPDATA%\deskbeat\deskbeat.log. The lyrics cache is in the same folder and is safe to delete.
If word-by-word lyrics stop while line-by-line ones still work, the community-run servers are the first thing to check: they come and go, which is why the list is a setting.
How it works
Where the data comes from
| What | From | Detail |
|---|---|---|
| Track, position, art and transport | The Windows media session API (GSMTC) | Filtered to Spotify's session. There is no Spotify Web API, no client ID and no OAuth, so it works on free accounts and needs no setup. |
| Audio | WASAPI process loopback | On Spotify's process tree, so the visualizer moves to Spotify and not to a game or a call. System loopback is a setting for when that is wanted. |
| Lyrics | LRCLIB, then a LyricsPlus server | LRCLIB answers first, with the time each line starts. A LyricsPlus server, when it has the track, then replaces that with the time each syllable is sung. |
How it works
Lyrics sources
LRCLIB knows when each line starts and nothing more, so word timing built from it alone is a guess: a line's time is spread over its words by length, and is marked as a guess. A LyricsPlus server carries the time every syllable is sung, and when it has the track that replaces the guess. Each word then fills in as it is sung and stays lit afterwards.
Word timing from a server is used as it is: words can overlap, as with background vocals, and can leave gaps.
The cache
Both answers are cached on disk, in %LOCALAPPDATA%\deskbeat. A miss is remembered for a week. A server that is down is not remembered as a miss, so the track is asked for again next time.
The servers
The servers asked for word timing are word_servers under [lyrics], tried in order. They are mirrors run by volunteers, and they come and go, which is why the list is a setting and not only a constant in the code. When none answers, lyrics fall back to line timing from LRCLIB. word_sync = false turns the extra lookup off.
Keeping lyrics in time
The media session reports a position now and then, not continuously, and the reports jitter. The sync clock advances on its own between reports, ignores jitter inside a deadband, takes the median of recent errors so one outlier cannot move it, eases small corrections in over a few frames, and snaps on a seek or a resume.
If a track's lyrics run early or late anyway, Ctrl+Alt+Up and Ctrl+Alt+Down move them 100 ms at a time.
How it works
Architecture
crates/core
Pure logic, no Windows types, unit tested anywhere
lrc
LRC and enhanced LRC parsing
timing
Line ends, word timing, the invariants the renderer relies on
clock
The sync clock: where the track is right now
lrclib · lyricsplus
LRCLIB requests, title cleaning and choosing among results; word-timed lyrics, request and response parsing
bands
FFT bins to bars, smoothing
color · timefmt
Hex, HSV, the accent taken from album art; clock formats
config
The TOML schema, defaults, clamps, migrations
crates/app
The exe: everything that needs a window, a device or Spotify
UI thread
Message loop, windows, Direct2D drawing, settings panel
media thread
Windows media session events for Spotify
capture thread
WASAPI loopback, FFT, bands
lyrics thread
One short-lived thread per track
The split
crates/core never names a Windows type. That is a rule, not a tendency: it is what lets every test in it run on any machine in milliseconds, and it keeps the parts that are easy to get subtly wrong, such as timing and config migration, away from the parts that can only be checked by looking.
crates/app is everything that needs a window, a device or Spotify.
Settings and state
The settings panel is an ordinary window drawn by the app itself, immediate-mode: each frame it lays its rows out from the config and writes changes straight back.
How it works
The loop
A widget's tick returns what it wants next:
| Answer | Means |
|---|---|
| Idle | Nothing until an event arrives. |
| After(duration) | Wake me then. The clock asks for the next minute, the lyrics for the next line or word boundary, the player for the next second. |
| Frame | Something is moving. The loop waits on the compositor's vblank and comes back. |
The most impatient answer wins. When every widget is idle the thread blocks on the message queue and uses no CPU.
How it works
Drawing
Widgets sit at the bottom of the window stack, above the desktop and under every app. A widget under a maximized or fullscreen window is not drawn. On a laptop with two GPUs it uses the integrated one.
The clock and the player have no box. Each of their elements has its own position, and the window is wrapped around whatever was drawn. In edit mode that window covers the work area instead and does not move, so dragging one element cannot disturb the others; the space between elements passes clicks through. The lyrics and the visualizer keep a frame, because it is what sizes them.
How it works
Privacy and security
No account, no login, no telemetry
There is no Spotify client ID and no token. Track details and the play, pause and skip buttons go through the Windows media session API, the same one the volume flyout uses.
What leaves the PC is the track you are playing, to two lyric services
For each new track, its title, artist, album and length are sent over HTTPS to LRCLIB and, when word timing is on, to the LyricsPlus servers listed under word_servers in the config. Nothing else is sent: no identifier, no listening history, no audio. Turning the lyrics widget off stops both; word_sync = false stops the second.
The LyricsPlus servers are community-run mirrors
They are not a service with a contract. Whoever runs one sees the requests above, and your IP address as any web server does. The list is yours to edit or empty.
Audio never leaves the process
The visualizer captures Spotify's output through WASAPI loopback, turns it into bar heights and discards it. Nothing is recorded or written to disk.
Lyric text is treated as text
A response is parsed as JSON and drawn with DirectWrite. Nothing in it is executed, and nothing from it reaches a shell, a path or a URL.
It runs as you, with no elevation
It writes only its own folders, %APPDATA%\deskbeat and %LOCALAPPDATA%\deskbeat, and one value under HKCU\...\Run when Start with Windows is on.
No secrets in the repository
The release workflow has none to hold: its only credential is the job's own token.
Known limits
- The exe is not code-signed, so SmartScreen warns on first run. Verify a download against the release's
SHA256SUMS.txt, or withgh attestation verify. - Anyone who can write
%APPDATA%\deskbeat\config.tomlcan pointword_serversat a server of their choosing and learn what you play. That person can already run programs as you. - Fonts in
%APPDATA%\deskbeat\fontsare loaded by DirectWrite. A malicious font file is a risk for any program that renders it; only put fonts there that you trust.
Reporting a hole
Open a security advisory on GitHub; it reaches the maintainer privately. Please do not open a public issue for something exploitable. Expect an acknowledgement within a week.
This site
The site is plain static files. It sets no cookie, runs no analytics and loads nothing from another origin: the fonts are served from here, and its Content-Security-Policy allows only this origin.
Developing
Repository layout
git clone https://github.com/<you>/deskbeat.git
cd deskbeat
git remote add upstream https://github.com/akshit-bansal11/deskbeat.gitThe upstream remote is there to pull in later changes.
| Path | What | Needs Windows |
|---|---|---|
| crates/core | Pure logic with no Windows types: lyric parsing and timing, the sync clock, band mapping, colour, the config schema. Its tests run anywhere. | no |
| crates/app | The exe: windows, Direct2D drawing, the media session, audio capture, the settings panel. | yes |
| scripts/ | The quality gate. | |
| docs/ | The architecture document. | |
| packaging/ | The winget manifest. | |
| site/ | This website. |
Developing
Prerequisites
- Windows 11.
- The stable Rust toolchain, 1.89 or later, with
rustfmtandclippy, and the MSVC build tools thatrustupasks for. - PowerShell 7 (
pwsh), which runs the gate.
Where to start without Spotify
crates/core has no Windows types at all, and holds most of the logic that matters: lyric parsing and timing, the sync clock, band mapping, colour, the config schema and its migrations. Its tests run anywhere, in milliseconds.
crates/app needs Windows. The snapshot mode draws every widget from made-up data, with no GPU, no Spotify and no audio device. What it cannot show is anything in the app loop: dragging, the settings panel's pointer handling, the tray, live reload. Those need a real run.
Developing
The quality gate
pwsh scripts/check.ps1 # formats in place, then clippy (warnings are errors) and tests
pwsh scripts/check.ps1 -Ci # what CI runs: fails on unformatted code instead of fixing itIt writes locally because fixing your formatting is useful; CI must not write to your branch, so it fails instead. Clippy and the tests run with --locked: a gate that quietly rewrites Cargo.lock has checked a different build.
Developing
Running it locally
cargo run --releaseOnly one copy runs at a time. If a release build is already in your tray, quit it first, or the new one hands over to it and exits.
cargo run --release -- --snapshot snapshotsRenders every widget and the settings panel from made-up data, with no GPU or Spotify. It is how CI checks the drawing code, and the quickest way to see a visual change.
cargo test --workspaceDeveloping
Tests
crates/core carries most of them: the LRC parser on real, half-broken files; word timing and its invariants; the sync clock against drift, jitter, seeks and pauses; title cleaning; config defaults, clamps and migrations from older files; band mapping; colour.
crates/app tests what can be tested without a window: sample mixing, the lyric widget's place-in-the-song logic, the settings panel's hex field, the tray icon's pixels.
deskbeat.exe --snapshot <dir> renders every widget and the settings panel to PNG from made-up data, with no GPU, Spotify or audio device. CI runs it on every push. It proves the drawing code runs and lets a change be looked at; it does not exercise the app loop, a pointer, or a live session.
Developing
Contributing
- A bug, or something Deskbeat should do: open an issue. Open one before starting anything bigger than a fix, so the design can be talked through before code exists.
- A security hole: report it privately, as the security section above describes, not in a public issue.
- A change: make it on a branch of your fork, run the gate until it is clean, and open a pull request against
main.
Making a change
- Bring your fork's
mainup to date withupstream/main, then branch from it on your fork. - Keep
crates/corefree of Windows types. Anything that needs a window, a device or Spotify goes incrates/app. - A new setting is four things in one commit: the field with its default, its clamp in
Config::sanitized, a row in the settings panel, and a test if an older config file has to keep loading. - Add or change a test when you add logic, a branch or fix a bug. Prefer testing in
crates/coreover anything that needs a window. - Run
pwsh scripts/check.ps1until it is clean. Do not suppress a lint finding to get green. - After changing a
Cargo.toml, commit theCargo.lockthatcargorewrites. The gate uses--lockedand fails on a stale one. - Commit with a Conventional Commits subject (
feat:,fix:,docs:,ci:, with an optional scope such asfeat(lyrics):) and a body that says what changed and why, including what you rejected. - Add a line under Unreleased in
CHANGELOG.md. - Push the branch to your fork and open a pull request against
main. CI runs the gate, builds the exe and renders the snapshots. The maintainer reviews and merges.
Style
- No new dependency without a reason in the pull request. The whole workspace has seven.
- The
windowscrate stays on 0.62. Its API moves between minor versions; a bump is its own pull request. - Constants are named. A number that appears twice is a constant.
- Doc comments say why, not what the next line already says.
- A widget that asks for a frame while nothing on it is moving is a bug. It is the whole reason this app exists instead of a Rainmeter skin.
The full text is CONTRIBUTING.md.
Developing
Releases
Pushing a v* tag runs the release workflow: the gate again, a release build, a check that the tag matches the crate version, the snapshot smoke test, then a GitHub Release carrying Deskbeat-<version>.exe, SHA256SUMS.txt and a build provenance attestation, with notes taken from that version's section of CHANGELOG.md.
Running the same workflow by hand attaches the build to a private draft release instead. Every action in every workflow is pinned to a commit, and Rust comes from the runner image, not from an action.
Reference
Design decisions
Reference
Known limits
- acceptedIt needs Windows 11 and the Spotify desktop app playing on the same PC. Playback sent to another device through Spotify Connect is not seen.
- acceptedThe exe is not code-signed, so SmartScreen warns on first run. A download can be verified against the release's SHA256SUMS.txt or its attestation.
- acceptedWord timing depends on community-run LyricsPlus servers. When none answers, lyrics fall back to line timing from LRCLIB.
- acceptedMemory use measured about 54 MB, above the 40 MB this was aiming for.
- unverifiedThe settings panel, the colour picker, and dragging and multi-selecting elements in edit layout.
- unverifiedThe word-by-word fill staying in time with a song as it plays.
- unverifiedThe transport buttons and seeking.
- unverifiedStaying visible through Show Desktop (Win+D).
- unverifiedSettings carrying over from the Sonic Veil name.
Tested on a real Windows 11 desktop
- All four widgets draw, sit on the desktop layer, and cost no CPU while idle.
- Lyrics lookup, title cleaning and the disk cache, against LRCLIB, and word timing from a LyricsPlus server.
- Audio capture, both for one app and for the whole system.
- Reading the track, position and album art from a running Spotify.
- An older config file loading with its sizes and fonts intact.
Reference
Version history
1.1.0
2026-10-04Any text of the clock and the player can run across, down or up, with its letters upright or lying sideways. A menu on a right-click of the tray icon: the name and version, a link to the repository, the start-with-Windows switch and Quit. A website, with the documentation on one page. A new mark, four sound bars that draw the letter D, in the three colours everything of the app's own now uses: ink, paper and one accent; the tray icon, the tray menu and the settings panel use the same three. In edit layout every element of the clock and the player moves alone, and Shift+click moves several as one; dragging one element no longer makes the others jump or shimmer.
1.0.1
2026-10-03No change to the app itself. The release file is named for its version, releases are published by the workflow's own token, every action is pinned to a commit, and the repository gained its quality gate script, draft builds, contributing and security documents, Dependabot and a winget manifest.
1.0.0
2026-10-03Renamed from Sonic Veil to Deskbeat, with settings carried over. Lyrics timed word by word from a LyricsPlus server, with LRCLIB as the fallback. A colour picker on every colour setting. Every element of the clock and the player has its own position and its own font, size, weight, colour, opacity, letter spacing and capitals setting; neither widget has a box. The visualizer can span the whole screen, with up to 512 bars, and be flipped. Fonts can be dropped into a folder instead of installed. The tray menu was removed: a double-click opens the settings panel, which holds everything the menu did.
0.1.0
2026-10-03First build, a pre-release. Four desktop widgets for Spotify in one native app, track details and controls from the Windows media session, Spotify-only audio capture, lyrics from LRCLIB with a disk cache, a settings panel, four one-click looks, an edit mode, a hand-editable config.toml that reloads when saved, global hotkeys, and the snapshot and probe diagnostics.
Reference
Licence and credits
- Line-timed lyrics come from LRCLIB. Word-timed lyrics come from community-run LyricsPlus servers.
- Built on windows-rs, RustFFT, serde and toml, all MIT or Apache 2.0.
- The clock's default pairing, Anurati and Quicksand, follows the Mond Rainmeter skin. Neither font is distributed with Deskbeat.
- Spotify is a trademark of Spotify AB. Deskbeat is not affiliated with or endorsed by Spotify; it reads what Windows reports about the app that is playing.
Deskbeat is free and stays free. The licence is MIT.