Why I built m17web
When introducing my fellow Austrian hams to M17, I kept hearing the same question: “How does it sound?” This coincided with my desire to experiment with Rust and WebAssembly, creating the perfect opportunity to build a tool that would answer this question while expanding my programming skills.
How it works
m17web consists of three main components connecting to mrefd and working together to bring M17 digital voice to your browser. The system listens to UDP traffic from M17 reflectors, routes it through a proxy server, decodes the audio in the browser using WebAssembly, and presents it through a clean web interface—all without requiring any special hardware or software installation.
Enhanced MREFD: Adding “listen-only” connections
Traditional M17 clients connect to mrefd by sending a packet with the “CONN” magic1, an amateur radio callsign, and the desired module. This approach has limitations for listen-only connections:
- Requires a callsign even for passive listening
- Appears in the reflector dashboard like any other connection
- Cannot be distinguished from interactive clients

Since version 0.12, MREFD supports a new “LSTN” magic connection type that:
- Uses a more permissive callsign filter
- Prevents stream forwarding from the client (enforcing listen-only status)
- Marks connections as listen-only in the XML output
- Allows dashboards to filter these connections in their displays

m17web-proxy: Bridging MREFD and browsers
The m17web-proxy serves as the critical middleware between MREFD’s UDP traffic and web browsers. It can:
- Connect to multiple modules across multiple reflectors
- Provide a WebSocket server for client connections
- Push module information updates to clients
Clients connect to the root WebSocket endpoint (wss://<proxy>/
) to receive information about all connected modules:

For specific module streams, clients connect to dedicated endpoints (wss://<proxy>/<reflector>/<module>
):

Importantly, the proxy only decodes metadata—the actual Codec2 stream is sent to the client for browser-side decoding.
m17web-wasm: Browser-based codec2 decoding
The m17web-wasm component leverages the codec2_rust crate to handle audio decoding. I just created a WebAssembly-compatible function that enables decoding directly in the browser.
m17web-client: The user interface
The m17web-client ties everything together with a user-friendly interface that:
- Connects to the m17web-proxy for module information
- Establishes module endpoint connections when users click play
- Processes incoming Codec2 streams through the WASM module
- Handles audio playback in the browser
- Manages multiple WebSocket connections for different instances
- Features a dark theme for night-time operation
Running your own instance
- Set up m17web-proxy:
- Follow build instructions in the repository or
- Use the provided Dockerfile (also available on DockerHub)
- Configure with environment variables:
M17WEB_PROXY_SUBSCRIPTION
: Set reflectors in formatDesignator_Modules,Designator_Modules,...
- Example:
M17-XOR_ABC,M17-M17_ACNT
- Implement m17web-client:
- Import the module
- Add the custom HTML elements to your website
- Configure through element attributes

With this setup, you’ll have a fully functional web-based M17 monitoring system that allows anyone to listen to M17 reflector traffic directly in their browser—no special equipment required!
Leave a Reply