- Rust 98.7%
- Nix 1.3%
| src | ||
| .gitignore | ||
| Cargo.lock | ||
| Cargo.toml | ||
| CLAUDE.md | ||
| config.example.toml | ||
| flake.lock | ||
| flake.nix | ||
| package.nix | ||
| README.md | ||
discord-overlay
A Wayland-native overlay that shows the members of your current Discord voice channel — avatar, name, mute/deafen badges, and a green ring around the speaker. Renders as a wlr-layer-shell surface, transparent, click-through. Includes a system tray icon (StatusNotifierItem) with Open config / Quit and a dynamic mic glyph that mirrors your own mute/deafen state.
Runs on any compositor that implements wlr-layer-shell (Hyprland, sway, KDE Plasma 6, …). Vanilla GNOME is unsupported. The tray icon needs a panel that speaks SNI — true on KDE, on most Wayland tiling setups via waybar, and on GNOME with the AppIndicator extension.
Setup
1. Register a Discord application
The overlay talks to your local Discord client over its IPC socket and uses OAuth to authorize. You need to register a Discord application to get the credentials.
- Go to https://discord.com/developers/applications.
- New Application → name it whatever (e.g.
discord-overlay). - In the left sidebar pick OAuth2.
- Under Redirects, add
http://127.0.0.1and Save Changes. The overlay never actually opens this URL — Discord just requires it to be present and to match what we send during the token exchange. - Copy the Client ID (visible at the top of the OAuth2 page).
- Click Reset Secret under Client Secret, then copy the secret. Treat it like a password — don't share it or check it in.
You don't need to submit the app for verification. Discord apps work for the developer-team account (you) without any approved-scopes review, which is exactly what we want.
2. Drop the credentials into config
mkdir -p ~/.config/discord-overlay
cp config.example.toml ~/.config/discord-overlay/config.toml
$EDITOR ~/.config/discord-overlay/config.toml
Paste the Client ID and Client Secret into client_id and client_secret.
3. First run
cargo run --release
On first launch, Discord pops up an Authorize dialog ("This will allow this app to read your voice channels …"). Approve it. The overlay caches the resulting access token at ~/.cache/discord-overlay/token.json (mode 0600), so subsequent launches don't re-prompt.
If you join a voice channel, you should see your channel's members appear in the overlay. Speaking lights up the green ring; muting/deafening yourself updates both the per-row badge and the tray icon.
Config reference
All keys live in ~/.config/discord-overlay/config.toml. See config.example.toml for the same in copy-paste form.
| Key | Type | Default | Notes |
|---|---|---|---|
client_id |
string | required | Discord application Client ID. |
client_secret |
string | required | Discord application Client Secret. |
anchor |
enum | "top-left" |
Where the overlay sits on screen. One of "top-left", "top-right", "bottom-left", "bottom-right". |
layer |
enum | "overlay" |
wlr-layer-shell stacking layer. "overlay" stays above fullscreen windows (and above screen lockers, deliberately); "top" is covered by fullscreen toplevels. |
width |
u32 | 300 |
Surface width in logical pixels. |
height |
u32 | 600 |
Surface height. Tall enough to fit your largest expected channel; rows past the edge are clipped. |
font_size |
f32 | 14.0 |
Name text size in pixels. Row height and the mute/deafen badge size scale with this. |
Logging
Tracing reads RUST_LOG. Default is info,discord_overlay=debug. To see voice-state events:
RUST_LOG=discord_overlay=trace cargo run
Token cache
The overlay stores the OAuth access + refresh tokens in ~/.cache/discord-overlay/token.json (mode 0600). To force a fresh authorization:
rm ~/.cache/discord-overlay/token.json
You'll get the Discord consent dialog again on next launch.
Avatar cache
Decoded avatars live at ~/.cache/discord-overlay/avatars/{user_id}_{hash}.png. Safe to wipe at any time; the overlay re-downloads as needed.