A professional browser-based SDR application with industry-standard visualizations, universal device support, and comprehensive testing.
Want to watch digital TV with your SDR? Follow our step-by-step ATSC Digital TV Golden Path Guide to learn the complete end-to-end workflow:
- 🔌 Connect your SDR device
- 🔍 Scan for ATSC channels in your area
- 📺 Tune and Play a channel
- 📋 View the Electronic Program Guide (EPG)
- 💬 Enable closed captions
- 📊 Monitor signal health and quality
Estimated time: 15-20 minutes | No SDR experience required!
→ Start the ATSC Golden Path Guide
- AM/FM/P25/Digital Mode Selection: Toggle between FM (88.1-107.9 MHz), AM (530-1700 kHz), P25 Phase 2 (700-800 MHz, 150-174 MHz), PSK31, and FT8
- Preset Stations: One-click tuning to popular stations
- Manual Frequency Control: Precise frequency adjustment
- Frequency Scanner: Automated scanning across user-defined ranges with signal detection and logging
- Device Configuration: LNA gain, amplifier control, sample rate
- IQ Constellation Diagram: Density-based heat mapping with Z-ordering
- Amplitude Waveform: Time-domain envelope with reference lines
- Power Spectral Density: Viridis colormap spectrogram
- Waterfall Display: Real-time scrolling frequency spectrum over time - perfect for identifying transient signals
- Interactive Controls: Pan, zoom, and multi-touch gestures for all visualizations
- Keyboard Navigation: Accessible controls for precision signal analysis
Modern GPU Acceleration:
- WebGPU Rendering: Modern GPU API for maximum performance (Chrome 113+, Edge 113+)
- WebGL Fallback: Wide browser support with hardware acceleration
- Progressive Enhancement: Graceful degradation to Canvas 2D for older browsers
- Zero Dependencies: Native browser APIs only - no external visualization libraries
- Browser-Native Transcription: Web Speech API integration for real-time speech-to-text
- AI-Ready Audio Stream: Clean demodulated audio optimized for recognition
- Multiple Language Support: Transcribe communications in various languages
- Robust Error Handling: Gracefully handles noisy/distorted radio audio
- P25 Phase 2 Decoder: H-DQPSK demodulation, TDMA slot extraction, frame synchronization
- Trunked Radio Support: Monitor talkgroups, control channels, and encrypted transmissions
- Digital Mode Decoders: PSK31 (BPSK/QPSK with Varicode), FT8 (stub with full architecture)
- Real-Time Text Display: Auto-scrolling message history with SNR and frequency indicators
rad.io features a comprehensive device integration framework that enables plug-and-play support for multiple SDR hardware platforms.
Supported Devices:
- HackRF One: Native WebUSB implementation (1 MHz - 6 GHz)
- RTL-SDR: Full support for RTL2832U-based devices (24-1766 MHz)
- Airspy: Database support (coming soon)
- Custom SDRs: Extensible architecture via
ISDRDeviceinterface
Framework Features:
- Universal Interface: All devices implement the same
ISDRDeviceinterface - Type-Safe Integration: Strict TypeScript with comprehensive type checking
- Plug-and-Play: Automatic device detection and configuration
- Memory Management: Built-in buffer tracking and cleanup
- Comprehensive Testing: Full test coverage for device implementations
Developer Resources:
- 📖 Add a New SDR Device Guide - Step-by-step instructions
- 📚 Architecture Documentation - Framework design and patterns
- 🗄️ State & Persistence Guide - Managing state across the app
- 📝 See existing device implementations in
src/models/for reference
Adding a new device takes ~200 lines of code following the patterns in existing implementations.
rad.io features an extensible plugin architecture that allows developers to add custom features without modifying the core codebase.
Plugin Types:
- Demodulator Plugins: Custom signal demodulation (FM, AM, SSB, digital modes)
- Visualization Plugins: Custom displays for signal analysis
- Device Driver Plugins: Add support for new SDR hardware
- Utility Plugins: General integrations and tools
Framework Features:
- Type-Safe: Full TypeScript support with strict type checking
- Lifecycle Management: Automated initialization, activation, and cleanup
- Configuration: Schema-based plugin configuration
- Event System: Plugin state change notifications
- Testing: Easy to unit test with Jest
Developer Resources:
- 📖 Creating Your First Plugin Tutorial - Step-by-step guide
- 📚 Plugin Developer How-To Guides - Demodulator, Visualization, and Device Driver plugins
- 🎨 Plugin Templates - Starter templates with examples
- 📐 Plugin System Architecture (ADR-0024) - Design decisions
- 💡 Example Plugins - Reference implementations
Creating a plugin takes ~100-200 lines of code using the provided templates and following established patterns.
- Comprehensive Unit Tests: Coverage across DSP, devices, and components
- CI/CD Pipeline: Automated lint, test (with coverage), format, build, and type-check
- Standard Visualization Interface:
IVisualizationRendererensures consistent rendering across WebGPU, WebGL, and Canvas backends - Zero External Visualization Dependencies: Native WebAudio API and Canvas rendering
rad.io is committed to providing a fully accessible experience for all users, including those using assistive technologies. The application follows WCAG 2.1 Level AA standards and implements modern web accessibility best practices.
- Full Keyboard Navigation: Complete control without a mouse - all features accessible via keyboard shortcuts
- Screen Reader Support: Comprehensive ARIA labels, semantic HTML, and live regions for real-time updates
- Focus Management: Clear 3px cyan focus indicators (≥3:1 contrast) with logical tab order
- Skip Links: Jump directly to main content (first tab stop)
- Color Accessibility: WCAG AA compliant contrast (4.5:1 text, 3:1 UI) with colorblind-safe palettes (Viridis)
- Responsive & Scalable: Works at 200% browser zoom, touch targets ≥44×44px on mobile
- Reduced Motion: Respects
prefers-reduced-motionfor users sensitive to animations
- 36 Automated Tests: jest-axe + manual ARIA/keyboard tests (all passing)
- E2E Testing: @axe-core/playwright for full-page accessibility scans
- ESLint Enforcement: 25+ jsx-a11y rules enforced in CI/CD
- Zero Critical Violations: Continuous monitoring with automated tools
- Manual Testing: Quarterly screen reader testing (NVDA, VoiceOver)
- Continuous Compliance: Documented processes for ongoing accessibility (ADR-0023)
- ACCESSIBILITY.md - Feature documentation and user guide
- ACCESSIBILITY-TESTING-GUIDE.md - Testing procedures for contributors
- ADR-0017 - Accessibility patterns
- ADR-0023 - Continuous compliance process
Compliance Badge: WCAG 2.1 AA Compliant ✓
rad.io uses the Diátaxis framework to organize documentation by user needs. Find exactly what you need:
📚 Tutorials - Learn by Doing
Step-by-step guides for beginners:
- Getting Started - Set up and run rad.io
- Your First Visualization - Build a spectrum analyzer
- More tutorials →
🔧 How-To Guides - Solve Problems
Task-focused guides for specific goals:
📖 Reference - Look Up Details
Technical specifications and API docs:
💡 Explanation - Understand Why
Design rationale and concepts:
Complete documentation index: docs/README.md
New to the project? Start here:
- 📚 New Contributor Onboarding Guide - Get started in minutes
- 🏗️ Visualization Architecture - Understand the system design
- 🧪 Testing Strategy - Learn how to test your code
- 🔌 E2E Testing Guide - Test with and without hardware
- Modern web browser with WebUSB support (Chrome 61+, Edge 79+, Opera 48+)
- HTTPS context (required for WebUSB)
- Compatible SDR device (HackRF One, RTL-SDR, etc.) - optional for development
# Clone repository
git clone https://github.com/alexthemitchell/rad.io.git
cd rad.io
# Install dependencies
npm install
# Start development server (HTTPS)
npm startThe development server runs over HTTPS at https://localhost:8080 by default.
# Unit tests (no hardware required)
npm test
# E2E tests with simulated device (no hardware required)
npm run test:e2e
# E2E tests with real HackRF (requires hardware)
export E2E_REAL_HACKRF=1
npm run test:e2eSee Testing Documentation for comprehensive testing guide.
# Build optimized bundle
npm run build
# Output in dist/ directory- Connect Hardware: Plug in your SDR device via USB
- Click "Connect Device": Browser will show device selection dialog
- Select Your Device: Choose your SDR from the list
- Grant Permission: Allow the web app to access the device
- Select signal type (FM/AM)
- Click a preset station button
- Device automatically tunes to the frequency
- Enter frequency in the input field
- Units automatically adjust (MHz for FM, kHz for AM)
- Press Enter or click away to apply
- Ensure device is connected
- Click "Start Reception" button
- Visualizations update with live data
- Click "Stop Reception" to pause
The automated frequency scanner sweeps through a user-defined range to detect and log active signals with automatic signal type classification.
Configuration:
- Start Frequency: Lower bound of scan range (MHz)
- End Frequency: Upper bound of scan range (MHz)
- Step Size: Frequency increment between scans (kHz)
- Detection Threshold: Signal strength required to log a signal (0-100%)
- Dwell Time: Time spent on each frequency (ms)
Usage:
- Configure scan parameters (default: 88-108 MHz, 100 kHz steps, 30% threshold)
- Click "Start Scan" to begin automated scanning
- Active signals are detected and logged in the table
- Use "Pause" to temporarily halt scanning
- Use "Resume" to continue from paused state
- Use "Stop" to end scanning completely
- Export results to JSON for further analysis
Active Signals Table:
- Lists all detected signals sorted by strength
- Shows frequency, signal strength percentage, signal type, and detection time
- Signal Type Classification: Automatically identifies modulation type with confidence score
- WFM (Wideband FM): 150-250 kHz bandwidth - Commercial FM radio broadcasts
- NFM (Narrowband FM): 12-30 kHz bandwidth - Two-way radio, amateur repeaters
- AM: 4-12 kHz bandwidth - AM radio, aviation, amateur bands
- Digital: 1-5 kHz with sharp edges - Digital voice modes, data transmissions
- Unknown: Signals that don't match known modulation patterns
- Color-coded strength bars: Red (weak), Orange (moderate), Green (strong)
- "Export" button saves results as JSON file with classification data
- "Clear" button removes all detected signals from the list
Note: Device must be connected and scanning only works for FM/AM modes (not P25).
- Shows I (in-phase) and Q (quadrature) signal components
- Density-based coloring: blue (sparse) → cyan → white (dense)
- Circular pattern = FM signal
- Varying magnitude = AM modulation
- Distinct points = Digital modulation
- Time-domain signal envelope
- Red line = Maximum amplitude
- Orange line = Average amplitude
- Green line = Minimum amplitude
- Useful for: AM detection, signal strength monitoring
- Frequency spectrum over time
- Color scale: Purple (low power) → Yellow (high power)
- Horizontal axis = Time
- Vertical axis = Frequency
- Bright bands = Strong signals
- Real-time scrolling frequency spectrum visualization
- New FFT frames appear at the top and scroll down
- Perfect for identifying transient signals and monitoring activity
- Toggle between static spectrogram and waterfall modes with one click
- Configurable buffer size (default: 100 frames)
- GPU-accelerated WebGL rendering for smooth performance
All visualizations support advanced pointer and wheel events for intuitive exploration:
Mouse & Pointer:
- Pan: Click and drag to move the view
- Zoom: Use mouse wheel to zoom in/out
- Reset: Click "Reset View" button when transformed
Touch & Multi-Touch:
- Pan: Single finger drag
- Pinch-to-Zoom: Two finger pinch gesture
- Tap: Focus on specific signal features
Keyboard Navigation (for accessibility):
- Arrow Keys: Pan in any direction (←, →, ↑, ↓)
- +/-: Zoom in and out
- 0: Reset to default view
Interactive controls are implemented via src/hooks/useVisualizationInteraction.ts and used across visualization components such as Spectrogram, IQConstellation, and WaveformVisualizer.
The Spectrum Explorer includes professional-grade measurement and analysis capabilities:
Interactive Markers 📍
- Single Click on spectrum to place a marker at the nearest peak
- Markers automatically display:
- Exact frequency (MHz with 6 decimal places)
- Power level (dB)
- Delta frequency between consecutive markers
- Delta power between consecutive markers (color-coded: blue for gain, red for loss)
- Remove Individual Markers: Click "Remove" button in the marker table
- Clear All Markers: Click "Clear Markers" button
- Export to CSV: Save all marker measurements for analysis in spreadsheet software
Peak Hold Mode 📈
- Enable with the "Peak Hold" checkbox
- Captures and displays the maximum power detected at each frequency bin over time
- Essential for:
- Identifying intermittent signals
- Measuring peak power of burst transmissions
- Finding hidden signals in noisy environments
- Clear Peak Hold: Reset accumulated peak data with the "Clear Peak Hold" button
- Keyboard Shortcut: Press
Pto toggle peak hold
Measurement Workflow Example:
- Enable Peak Hold to capture signal peaks
- Click on signals of interest to place markers
- Review frequency spacing and power differences in the marker table
- Export measurements to CSV for documentation or further analysis
These tools are particularly useful for:
- Bandwidth measurement (mark signal edges and read delta frequency)
- Channel spacing verification
- Relative signal strength comparison
- Interference analysis
- Spectrum occupancy studies
- Tune to a voice transmission (FM/AM)
- Ensure good signal strength
- Speech recognition automatically transcribes audio
- View transcripts in real-time
Supported Use Cases:
- Public safety radio monitoring
- Aviation communications
- Amateur radio logging
- Emergency broadcast transcription
- Multi-language monitoring
Note: Web Speech API requires Chrome/Edge browsers and may request microphone permission.
- Select "P25" signal type
- Configure system parameters:
- Control Channel frequency (e.g., 770.95625 MHz)
- NAC (Network Access Code)
- System ID
- WACN (Wide Area Communications Network)
- Add talkgroups to monitor
- Start reception to decode transmissions
- Phase: Shows P25 Phase 2 when decoding TDMA signals
- TDMA Slot: Indicates which time slot (1 or 2) is active
- Signal Quality: 0-100% based on constellation accuracy
- Encryption: Shows if transmission is encrypted
- Talkgroup: Active talkgroup ID and name
P25 Features:
- H-DQPSK Demodulation: Differential QPSK with 6000 symbols/sec
- TDMA Slot Extraction: Separates two simultaneous voice channels
- Frame Synchronization: Detects P25 frame boundaries
- Signal Quality Metrics: Real-time constellation analysis
- Talkgroup Scanning: Monitor multiple talkgroups simultaneously
Frequency Bands:
- 700 MHz Band: 764-776 MHz, 794-806 MHz (most common)
- 800 MHz Band: 851-870 MHz (also common)
- VHF Band: 150-174 MHz (rural/legacy systems)
- UHF Band: 450-470 MHz (some regions)
See implementation in src/utils/p25decoder.ts for technical details and API surface.
The DSP pipeline visualization shows the complete signal flow:
- RF Input: Antenna signal reception
- Tuner: Frequency selection
- I/Q Sampling: Digital conversion
- FFT: Frequency analysis
- Demodulation: Signal extraction
- Audio Output: Speaker/headphones
- React 19: UI framework
- TypeScript: Type-safe development
- WebUSB API: Hardware communication
- WebAudio API: DSP processing
- HTML Canvas: High-performance rendering
- Jest: Testing framework
- Webpack: Build tooling
- GitHub Actions: CI/CD pipeline
You can enable/disable the WebAssembly DSP path and its output validation at runtime via localStorage flags (no rebuild required). This is useful for debugging and CI stability.
radio.wasm.enabled: set tofalseto force JavaScript fallbacks; any other value (or unset) enables WASMradio.wasm.validate: set totrueto enable O(N) output validation (dev default: on; prod default: off)
See docs/reference/wasm-runtime-flags.md for details on behavior, validation heuristics, and dev cache-busting.
- ✅ Google Chrome 61+
- ✅ Microsoft Edge 79+
- ✅ Opera 48+
- ❌ Firefox (WebUSB not implemented)
- ❌ Safari (WebUSB not implemented)
Note: HTTPS context is required for WebUSB access.
- PowerShell is the default shell; commands shown use npm scripts so they work the same across platforms.
- The
cleanscript usesrimraffor cross‑platform deletion ofdist,node_modules, andbuild.
For AI agents contributing to this repository, use Serena memories to keep context lean and high-signal:
- Start by listing available memories and look for "memory_usage".
- Read that memory for concise best practices on minimizing noise-to-signal in context, retrieval order, and when/what to write.
- Only write new memories for durable, reusable knowledge (architecture decisions, debugging playbooks, repo-wide workflows). Prefer updating an existing memory over creating duplicates.
- Favor symbol-first exploration over full-file reads. Avoid re-reading the same content with multiple tools.
See .github/copilot-instructions.md for detailed agent workflows and available tools.
rad.io is automatically deployed to GitHub Pages on every push to main. The deployment process includes:
- Automated build and validation
- CDN distribution via GitHub Pages
- Post-deployment health checks
- Artifact management and cleanup
For detailed deployment documentation, see docs/DEPLOYMENT.md, which covers:
- Automated and manual deployment procedures
- Build artifact management
- Post-deployment validation
- Rollback procedures
- Troubleshooting guides
We welcome contributors of all experience levels!
- 💬 GitHub Discussions - Ask questions, share ideas, and connect with other users
- 🐛 Report Issues - Found a bug? Let us know!
- ✨ Request Features - Suggest new functionality
- 🤝 Contribute - Help improve rad.io
New Contributors:
- Read the Community Guidelines - Learn how we work together
- Follow the First-Time Contributor Checklist - Step-by-step guide
- Check the Onboarding Guide - Technical setup and walkthrough
- Find a
good first issue- Start contributing!
Contributors are recognized in CONTRIBUTORS.md
- Community Guidelines - How we work together and communicate
- Contributing Guide - Detailed contribution guidelines
- Code of Conduct - Our community standards
- Support - Getting help and support resources
- Governance - How decisions are made
- Security Policy - Reporting security vulnerabilities
Issue & PR Templates:
We're planning monthly community calls! Stay tuned for announcements in Discussions.
- GitHub’s guidance for accessible profile/README content: https://github.blog/developer-skills/github/5-tips-for-making-your-github-profile-page-accessible/
- Key ideas we follow here: descriptive image alt text, meaningful link text (no “click here”), clear heading hierarchy, and emojis that complement the text (not replace it).