Send audio between computers over your local network — system audio or a microphone, in either direction, with low latency.
Windows and Linux. Free and open source (MIT).
| Send system audio | Play your PC's sound on another computer's speakers |
| Send microphone | Stream a mic from one machine to another |
| Listen | Hear the incoming audio on this computer's speakers |
| Use as microphone | Make a remote mic appear as a microphone to Discord, Zoom, OBS… |
| Headset mode | One button turns another machine's headset into this machine's headset — mic and speakers, both directions |
Devices find each other automatically on the LAN — no IP addresses to type. The app lives in the system tray and keeps streaming when you close the window. Interface available in 10 languages.
Latency. The software path measured 12–21 ms in a local loopback test on Linux, depending on the audio graph quantum (docs/10-riskler.md). End to end between two machines with default settings, expect roughly 60–90 ms — the jitter buffer contributes 40 ms by default and each machine's device buffer 20 ms or so. Lower the buffer and the device period if you need less.
RelAudio was written after living with AudioRelay — a polished app that solves this problem well, has been doing it for years, and supports phones too. Credit where it's due: it's what showed me this workflow was possible at all, and if you want a mature, supported product with Android and iOS clients, go use it and pay for it.
This project is an open-source take on the same idea, deliberately narrower: Windows ↔ Linux desktops, and one workflow in particular — driving a remote machine while your headset stays plugged into the one in front of you. It exists because I wanted the source, wanted that specific case handled end to end, and wanted the awkward parts (which cable end, which default device, why you hear yourself) explained rather than left to trial and error.
Different scope, not a competitor. If AudioRelay does what you need, use it.
You need Rust, Node.js, git, and the PulseAudio/PipeWire + WebKitGTK development headers. Run the block for your distribution:
# Arch / CachyOS
sudo pacman -S --needed base-devel git rustup nodejs npm libpulse webkit2gtk-4.1 libayatana-appindicator
rustup default stable
# Debian / Ubuntu
sudo apt install build-essential curl git libpulse-dev libwebkit2gtk-4.1-dev \
libayatana-appindicator3-dev librsvg2-dev nodejs npm
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh # Rust is not in apt
. "$HOME/.cargo/env"
# Fedora
sudo dnf install @development-tools git pulseaudio-libs-devel webkit2gtk4.1-devel \
libappindicator-gtk3-devel librsvg2-devel nodejs
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh # Rust is not in the default repos
. "$HOME/.cargo/env"Then build and install. All three build commands matter — the second one builds the diagnostic tool that every Troubleshooting command below uses:
git clone https://github.com/bbesli/RelAudio.git
cd RelAudio
cargo build --release --bin relaudio-cli # diagnostics: devices, tone, level
cd app && npm install && npx tauri build --no-bundle
cd .. && ./scripts/install-linux.shThis installs the app to ~/.local/bin/relaudio, the diagnostic tool to
~/.local/bin/relaudio-cli, and adds a RelAudio entry to your application
menu. No root needed. Remove it with ./scripts/uninstall-linux.sh.
If
relaudioisn't found in your shell,~/.local/binisn't on yourPATH. Either launch it from the application menu, or add the directory to your shell's startup file —~/.bashrcon most distributions,~/.zshrcif you use zsh:echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc && exec $SHELL
.deb and .rpm packages can be built with npx tauri build (AppImage
generation currently fails — see Known limits).
You need:
- Rust — run
rustup-init.exe, choose the standard installation. - Visual C++ Build Tools with the Windows SDK. If
rustupdoesn't offer to install them:Thewinget install --id Microsoft.VisualStudio.2022.BuildTools -e --override "--quiet --wait --add Microsoft.VisualStudio.Workload.VCTools --includeRecommended"--add ...VCToolspart matters — without it you get Build Tools with no C++ compiler and the build fails withlink.exe not found. - Node.js
- Git — or
winget install --id Git.Git -e
Then:
git clone https://github.com/bbesli/RelAudio.git
cd RelAudio
powershell -ExecutionPolicy Bypass -File scripts\build-windows.ps1
The script checks prerequisites, loads the Visual Studio environment (including preview/Insiders editions, which Rust can't find on its own), installs npm dependencies and builds. It produces two programs:
| File | What it is |
|---|---|
app\src-tauri\target\release\relaudio-app.exe |
the app you double-click |
target\release\relaudio-cli.exe |
the diagnostic tool used in Troubleshooting |
Neither is on your PATH. To run a Troubleshooting command, open PowerShell in
the RelAudio folder you cloned and prefix it with .\:
cd C:\path\to\RelAudio
.\target\release\relaudio-cli.exe devices
Below, relaudio-cli is written without the path for readability — on Windows
substitute .\target\release\relaudio-cli.exe.
Firewall: the first time you receive audio, Windows will ask to allow the app. Say yes for private networks. To add the rule manually, in an administrator PowerShell:
New-NetFirewallRule -DisplayName "RelAudio UDP 59101" -Direction Inbound -Protocol UDP -LocalPort 59101 -Action Allow
If you turn on Allow remote start (see Scenario 3), the one-button setup also needs TCP 59100:
New-NetFirewallRule -DisplayName "RelAudio control TCP 59100" -Direction Inbound -Protocol TCP -LocalPort 59100 -Action Allow
Two things to know, because almost every problem people hit comes from one of them.
1. A "virtual audio cable" is what lets an app become a microphone. Neither Windows nor Linux lets a normal program appear in another program's microphone list. A virtual cable is a driver with two ends: one end looks like a speaker, the other looks like a microphone. Whatever is written to the speaker end comes out of the microphone end.
RelAudio writes here your meeting app reads here
│ │
▼ ▼
CABLE Input ══════ the cable ══════► CABLE Output
(a speaker) (a microphone)
The names are confusing on purpose-ish: they are named from the cable's point
of view, not Windows'. So in Windows' Sound panel, CABLE Input is on the
Playback tab and CABLE Output is on the Recording tab. If you go
looking for CABLE Output in a list of speakers, you will not find it.
2. One machine must never capture the same device it writes to. If it does, the audio feeds itself in a circle and you hear your own voice. RelAudio's Headset mode picks devices for you and never breaks this rule; if you configure things by hand, this is the rule to keep in mind.
Open RelAudio on both computers. Each one starts listening automatically and announces itself on the network, so they find each other — you never type an IP.
Below, "machine A" and "machine B" just mean your two computers.
Example: your PC plays music, you want to hear it on the laptop in the kitchen.
On the machine that should make the sound (B):
- Open RelAudio.
- Click the Player tab.
- Make sure Listen on speakers is selected.
- On the right, under Output device, choose your speakers or headphones.
- Press Start listening. (It usually already started by itself.)
On the machine whose sound you want to send (A):
- Open RelAudio.
- Click the Server tab.
- Choose System audio.
- In Target device, pick machine B from the list. If the list is empty, wait a few seconds. Still empty? See Troubleshooting.
- Press Start streaming.
Play something on A. You should hear it on B within a second.
On Linux, "system audio" means the monitor of an output device. RelAudio picks the default one, which is right in most cases. If you hear nothing, check which output your media player is actually using — some apps are pinned to a specific device.
Exactly like Scenario 1, except in step 3 on machine A you choose Microphone instead of System audio, and on the right you pick which microphone to send.
This only makes the mic audible on B. If you want B's apps (Discord, Zoom…) to treat it as a real microphone, that's Scenario 4.
Example: you are sitting at your Linux machine, controlling a Windows machine over Parsec / RDP / Sunshine. Your headset is plugged into the Linux machine. You want it to work as the Windows machine's headset — you speak into it, the meeting hears you; the meeting talks, you hear it.
This needs both directions at once, and RelAudio sets both up for you.
Only the machine without the headset needs one.
Windows:
- Go to https://vb-audio.com/Cable/.
- Download VBCABLE_Driver_Pack (the ZIP on the left, under Windows).
- Extract the ZIP to a folder. Do not run it from inside the ZIP.
- Right-click
VBCABLE_Setup_x64.exe→ Run as administrator. - Click Install Driver, accept the Windows prompt.
- Reboot. The driver is not fully usable until you do.
After rebooting you should see CABLE Input in your speaker list and
CABLE Output in your microphone list.
If another app has left its own virtual audio device behind (AudioRelay, VoiceMeeter, Virtual Audio Cable…), RelAudio prefers the one this guide told you to install —
RelAudio-Cablefirst, then VB-CABLE, then anything else. You can always override it in the Devices panel on the right; your choice is saved and wins over the automatic pick.
Linux: nothing to install. Run this once (it lasts until reboot):
pactl load-module module-null-sink sink_name=relaudio \
media.class=Audio/Sink sink_properties=device.description=RelAudio-CableIf that fails with
invalid argument, you are on classic PulseAudio rather than PipeWire; drop themedia.classpart:pactl load-module module-null-sink sink_name=relaudio \ sink_properties=device.description=RelAudio-Cable
The cable's two ends are named differently from Windows. Your speaker list gets
RelAudio-Cable; your microphone list gets Monitor of RelAudio-Cable —
that second one is what you pick in your meeting app. To make it survive
reboots, put the same line (without pactl) in
~/.config/pipewire/pipewire-pulse.conf.d/relaudio.conf, or just re-run it
after each boot.
Do this. Parsec, RDP, AnyDesk and friends capture the remote machine's default speaker and stream it to you. If RelAudio is also carrying audio you get everything twice — and if the remote desktop happens to capture the very cable RelAudio writes into, you will hear your own voice and nothing you change in RelAudio will fix it.
- Parsec: Settings → Host (or Client) → Audio → off.
- Windows RDP: in the connection settings, Local Resources → Remote audio → Do not play.
RelAudio replaces that audio channel, with lower latency.
On the remote machine, open Sound settings and make sure the default output is
your normal speakers (e.g. Speakers (Realtek(R) Audio)) — not the cable.
Why: RelAudio captures the system sound from a real output device and sends it to you. If the default were the cable, the machine's audio would go into the cable instead, mix with the relayed microphone, and you would hear yourself.
- Windows: press Win+R, type
mmsys.cpl, Enter. Playback tab → click your speakers → Set Default. - Linux: System Settings → Sound → set the output device to your real
speakers, not
RelAudio-Cable. From a terminal:pactl set-default-sink <your real sink>(list them withpactl list short sinks).
On the machine where the headset is plugged in:
- Headset tab.
- Choose Headset is on this machine.
- Other device → pick the remote machine.
- Press Start headset mode.
RelAudio picks this machine's default microphone and default speakers. So make the headset the default here first — otherwise you send your laptop's built-in mic and hear its built-in speakers. If you'd rather not change the system default, open Devices in the panel on the right and pick the headset's mic and speakers by hand; your choice is saved and wins.
The first time only, you pair the two machines. Instead of starting, this machine shows a 6-digit code:
Type this code on WINDOWS-PC
418 205
Go to the other machine, open RelAudio, Headset tab, pick this machine in Other device, and type the code into the box that appears. That's it — the two machines share a key, and the code is never asked for again. The first machine notices and starts on its own.
Why there's a code. Without it, anything on your network could tell your machine to capture audio and stream it away. Pairing means only the machines you deliberately introduced to each other can do that. The code is good for 3 minutes and dies after 5 wrong guesses. You can see and remove paired devices in Settings.
Honest limit: the pairing exchange itself is not encrypted, so someone who can capture that one moment of traffic could take over the pairing. The audio stream is not encrypted either. This protects you from anyone who can connect to your machine, not from someone who can sniff your network.
Every session after that is just: pick the machine, press Start headset mode. The other machine starts its half automatically and the panel says "Both sides running — WINDOWS-PC was started remotely", along with the microphone name to pick in your meeting app. Pressing Stop stops both machines.
If the other machine could not be started you get a box saying exactly why, and this machine keeps running — so you can still go over and start it by hand, the old way, which continues to work.
Only if remote start did not work — go to the remote machine and do it by hand:
- Headset tab.
- Choose Remote machine.
- Other device → pick the machine with the headset.
- Press Start headset mode.
Before you press start, RelAudio shows which two devices it picked. On the remote machine that should look like:
Source Speakers (Realtek(R) Audio) ← its system sound, sent to you
Output CABLE Input (VB-Audio Virtual Cable) ← your mic, written into the cable
Two different devices. That is the rule from the top of this page, enforced.
On the remote machine, in Discord / Zoom / Teams / Meet:
- Microphone: the capture end of the cable —
CABLE Output (VB-Audio Virtual Cable)if the remote machine is Windows,Monitor of RelAudio-Cableif it is Linux. RelAudio tells you the exact name on screen after you press start; use that. - Speaker / output: leave it as your normal speakers. Do not pick the cable. Its sound is already being relayed to you.
That's it. Talk into your headset — the meeting hears you. The meeting talks — you hear it in your headset.
Same as Scenario 3 but you only care about the microphone direction (you already have sound some other way).
- Install a virtual cable on the receiving machine (Step 1 above).
- Receiving machine: Player tab → Use as microphone → the device list
filters itself to cables → pick
CABLE Input. Press Start listening. A green box appears telling you which microphone to select elsewhere. - Sending machine: Server tab → Microphone → pick the target → Start streaming.
- In your app: choose
CABLE Outputas the microphone.
CABLE Outputwill not appear in RelAudio's list. RelAudio shows speakers;CABLE Outputis a microphone. It appears in the other app's microphone list. This trips up almost everyone once.
Work down this list in order. Each step tells you where the chain is broken.
This is the most common complaint, and it is almost never RelAudio playing the audio back at you. Check in this order:
-
Is your remote desktop still streaming audio? Parsec / RDP / AnyDesk send you the remote machine's speaker output. If RelAudio is relaying too, the same sound reaches you twice — and if the remote desktop captures the cable RelAudio writes into, you hear yourself no matter what you change. → Turn its audio off (Scenario 3, Step 2).
-
Is the remote machine's default speaker set to the cable? Then everything that machine plays goes into the cable, mixes with your relayed microphone, and comes back. →
mmsys.cpl→ Playback → pick your real speakers → Set Default. -
Is "Listen to this device" enabled on the cable's microphone end? That setting pipes the cable straight to your speakers. →
mmsys.cpl→ Recording tab →CABLE Output→ Properties → Listen tab → uncheck Listen to this device. -
Is the same meeting open on both machines? If Discord is in the same voice channel on both, one of them transmits your relayed microphone and the other plays it back to you. Leave the call on the machine that isn't relaying.
-
Is RelAudio itself looping? If the Server captures the same device the Player writes to, RelAudio shows a red Feedback loop warning. Headset mode prevents this; manual configuration can hit it.
Nothing is arriving. Either the sender is aiming at the wrong address, or a firewall is in the way.
- The Player tab shows this machine's address. Make sure the sender is pointed at exactly that.
- If the sending side shows a red Send errors count, the packets are being refused — wrong address. A machine with several network adapters (VPN, WSL, Hyper-V) announces more than one; pick the target again, or type the address by hand.
- Windows firewall: allow the app when prompted, or add the rule manually:
New-NetFirewallRule -DisplayName "RelAudio UDP 59101" -Direction Inbound -Protocol UDP -LocalPort 59101 -Action Allow
The audio is going somewhere you're not listening.
- Look at the Output row in the Statistics panel. That is the device the stream actually opened — not what the dropdown shows. If it says the wrong thing, change the device on the right; the stream restarts by itself.
- Test the output on its own, with no network involved:
If you don't hear a 440 Hz tone, the problem is the output device, not the network.
relaudio-cli tone - In Headset/microphone mode the audio goes into a cable, so you are not
supposed to hear it. Check the far end instead. Run
relaudio-cli devicesfirst to get the id, then:The bar should move when the other side speaks. (# Windows — CABLE Output is a real microphone, so --mic is right relaudio-cli level --mic --device "<CABLE Output id>" # Linux — the cable's capture end is a monitor, so leave --mic off relaudio-cli level --device "<Monitor of RelAudio-Cable id>"--micon Linux would look for a real microphone with that name and reportaygıt bulunamadı.)
The panel tells you which of these it was.
| What it says | What to do |
|---|---|
| not paired | Press start here, then type the 6-digit code on the other machine (Step 4). |
| does not accept remote start | On the other machine: Settings → Allow remote start is switched off. Turn it back on. |
| the clocks are too far apart | The signature carries a timestamp. Fix the system clock on either machine. |
| already has a session started by hand | Press Stop on the other machine, then try again. |
| has no virtual audio cable installed | Install VB-CABLE / create the null sink there (Step 1). |
| could not be reached | The two machines can see each other over mDNS but not over TCP 59100. Allow it in the firewall — on Windows: New-NetFirewallRule -DisplayName "RelAudio control" -Direction Inbound -Protocol TCP -LocalPort 59100 -Action Allow |
| version is incompatible | Update RelAudio on both machines. |
If the machine you picked never accepted remote start, RelAudio says so before you press the button, right under the device list.
- Did you reboot after installing VB-CABLE? It is not fully registered until you do.
- You are probably looking at a list of speakers. The capture end of the cable
—
CABLE Outputon Windows,Monitor of RelAudio-Cableon Linux — appears under microphone/input settings, never under speakers. - Some apps cache their device list — restart the meeting app.
| Symptom | Cause |
|---|---|
| App won't open, nothing happens | It's already running — check the system tray. RelAudio allows one instance. |
| No tray icon on GNOME | GNOME has no tray by default. Install the AppIndicator and KStatusNotifierItem Support extension. Without a tray, RelAudio disables "minimize to tray" so it can't become unreachable. |
| Buffer grows over time / "Dropped" counter climbs | Clock drift between the two machines. There is no compensation yet; restart the stream. |
| Sound is choppy | Raise Buffer in the Player tab. Each unit is 5 ms; the default 8 is 40 ms. Wi-Fi usually wants 15–20. |
| Anything else | Read the log. Settings tab shows the exact path — %LOCALAPPDATA%\RelAudio\relaudio.log on Windows, ~/.local/state/RelAudio/relaudio.log on Linux. |
- No clock drift compensation. The two machines' sound cards don't run at exactly the same rate. Over long sessions the buffer creeps and the "dropped" counter grows. A cap keeps latency bounded; adaptive resampling is planned.
- PCM only. ~1.5 Mbit/s, though silent blocks are sent without payload so quiet passages cost almost nothing. Opus is planned.
- Not encrypted. Use it on networks you trust.
- No packet loss concealment. Lost packets become short silences.
- macOS is not supported — deliberately deferred, see docs/00-genel-bakis.md.
- AppImage build fails (
linuxdeployerror)..deband.rpmwork.
capture ──► packetize ──► UDP ──► jitter buffer ──► playback
(WASAPI / PulseAudio) 5 ms (WASAPI / PulseAudio)
- UI: Tauri v2 + Svelte 5 — ~11 MB binary, uses the OS webview
- Core: Rust. Audio and network run on dedicated threads
- Linux backend: PulseAudio API (PipeWire provides it natively)
- Windows backend: WASAPI, loopback via a render device opened for capture
- Discovery: mDNS (
_relaudio._udp)
Design notes, measurements and decision records are in docs/ — written in Turkish. Architecture: docs/02-mimari.md. Decision records: docs/adr/.
cargo test # core tests
cd app/src-tauri && cargo test # session layer tests
rustup target add x86_64-pc-windows-msvc # once
cargo check --target x86_64-pc-windows-msvc # type-check Windows code on Linux
node scripts/check-i18n.mjs # translation completenessThat third one matters: Windows-specific code is cfg-gated out of normal
Linux builds, so it's never checked unless you ask for it.
MIT — see LICENSE.
If RelAudio is useful to you: buymeacoffee.com/bbesli