A lightweight, no-install mobile web application designed to handle the specific mix-minus audio routing needs of a podcast studio with three local hosts and one remote guest.
To prevent audio feedback loops and latency in the studio, the application uses a Selective-Mesh WebRTC topology. Rather than a full mesh where everyone connects to everyone:
- Remote Guest establishes a bi-directional connection to Host 1, 2, and 3.
- Hosts 1-3 only connect to the Guest. They do not connect to each other.
- This ensures that local studio hosts (who can hear each other naturally in the room) only receive the remote guest's audio, and the guest receives a mix of all three hosts.
Ensure you have Node.js installed, clone the repository, and install dependencies:
npm installTo start the signaling and static file server:
npm startBy default, the server runs on port 3003 (or checks the PORT environment variable).
- Local access:
http://localhost:3003
Open multiple browser tabs or devices on your network:
- Open tab 1: Select Host 1
- Open tab 2: Select Remote Guest
- The connection indicators will turn green (
Session Active), and audio level meters will begin rendering dynamically.
- Interactive Mode: Focus on the terminal window and press
Ctrl + C. - Background Mode (Kill by Port): If the server was run in the background or terminal was closed:
fuser -k 3003/tcp
- Kill all Node processes:
pkill node
You can run the application using Docker without installing Node.js locally:
Prerequisites: Docker and Docker Compose installed.
# Build and start the container
docker compose up -d
# View logs
docker compose logs -f
# Stop the container
docker compose downThe server will be available at http://localhost:3003.
When testing on public, university, or corporate Wi-Fi networks, you may encounter connectivity issues:
- AP/Client Isolation: Router prevents devices on the same Wi-Fi from talking to each other (local hosts can't reach the PC's IP).
- Firewall Web Filtering (e.g., Fortinet): Firewall blocks tunnels like Ngrok with security warnings (
net::ERR_CERT_AUTHORITY_INVALID).
- Turn on a Wi-Fi Hotspot on a mobile phone (using cellular data).
- Connect your Studio PC and the hosts' phones to this hotspot.
- This creates a direct local network with no blocks or AP isolation.
If you cannot use a hotspot and Fortinet is blocking Ngrok, run a tunnel over standard SSH using the free localhost.run service:
ssh -o StrictHostKeyChecking=no -R 80:localhost:3003 nokey@localhost.runHave local hosts and guests turn off Wi-Fi on their phones and connect to the generated public HTTPS link using their 4G/5G cellular data to bypass local network blocks.
To deploy this in your university cloud or public hosting behind an Apache reverse proxy:
Browsers strictly block microphone access (navigator.mediaDevices.getUserMedia) on insecure origins. The production server must be served over HTTPS with a valid SSL certificate.
You must proxy both standard HTTP traffic and upgrade WebSocket connection requests (ws:// / wss://). Enable the required modules:
sudo a2enmod proxy proxy_http proxy_wstunnel rewrite ssl headersAdd the following configuration to your VirtualHost file:
<VirtualHost *:80>
ServerName audio.youruniversity.edu
Redirect permanent / https://audio.youruniversity.edu/
</VirtualHost>
<VirtualHost *:443>
ServerName audio.youruniversity.edu
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/audio.youruniversity.edu/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/audio.youruniversity.edu/privkey.pem
ProxyRequests Off
ProxyPreserveHost On
# Proxy WebSocket Traffic first (Signaling)
RewriteEngine On
RewriteCond %{HTTP:Upgrade} =websocket [NC]
RewriteRule ^/(.*) ws://localhost:3003/$1 [P,L]
# Proxy HTTP Traffic (Frontend Files)
ProxyPass / http://localhost:3003/
ProxyPassReverse / http://localhost:3003/
# Security Headers for Device Access Permissions
Header always set Referrer-Policy "no-referrer-when-downgrade"
Header always set Feature-Policy "microphone 'self'"
</VirtualHost>If university clients fail to establish audio routes (connections stay on Connecting... or Failed), firewalls are likely blocking direct UDP streams.
To resolve this:
- Set up a Coturn server on your cloud node.
- Update the WebRTC configuration array in
public/app.jsto include your TURN credential credentials:const rtcConfig = { iceServers: [ { urls: 'stun:stun.l.google.com:19302' }, { urls: 'turn:your-turn-server.edu:3478', username: 'your-username', credential: 'your-password' } ] };