A real-time voice communication Progressive Web App built with PHP.
- Real-time Voice Communication: Push-to-talk functionality with instant audio transmission
- WebAuthn/Passkey Authentication: Passwordless login with biometrics or security keys
- Multi-device support (Windows Hello, Touch ID, Android, YubiKey, etc.)
- Optional anonymous mode with auto-generated screen names
- Secure JWT-based session management
- User Identity: Screen names displayed in messages and history
- Unique screen names enforced across authenticated and anonymous users
- Persistent screen names for registered users
- Auto-generated random names for anonymous users (e.g., "BoldEagle742")
- Message History: Automatic recording and playback of recent transmissions
- Configurable retention (message count and age limits)
- Individual and sequential playback controls
- Visual history panel with timestamps and user identities
- Live updates as users speak
- Channel Support: Multiple channels with isolated message histories
- CLI Audio Tools: Powerful command-line utilities for automation and management
- Audio Sender: Programmatically send pre-recorded audio to any channel
- Welcome Messages: Automated greetings on user connect or channel join
- Bot Integration: Create audio bots for announcements and automated responses
- Multiple Formats: Support for WAV files and raw PCM16 audio
- Example Audio: Sample files included for testing (tones, notifications, welcome messages)
- Progressive Web App: Installable, offline-capable, responsive design
- Plugin System: Extend functionality with custom plugins (rate limiting, logging, custom features)
- Event-based architecture with hooks for server, connections, audio, channels
- Example plugins included (Hello World, Rate Limiter)
- Easy installation and configuration
- Embeddable: Can be embedded in iframes or linked directly
- Cross-platform: Works on desktop and mobile devices
- Live Demo
- Note: If you don't see the message history panel, perform a hard refresh (Ctrl+Shift+R on Windows/Linux, Cmd+Shift+R on Mac) or clear your browser cache to load the latest version.
Before installation, ensure you have:
- PHP 8.1 or higher with the following extensions:
pdo_sqlite- Required for message history storagembstring- For string handlingsockets- For WebSocket server
- Composer - For PHP dependency management
To check if extensions are installed:
php -m | grep -E 'pdo_sqlite|mbstring|sockets'To install missing extensions:
# Ubuntu/Debian
sudo apt-get install php-sqlite3 php-mbstring
# CentOS/RHEL
sudo yum install php-pdo php-mbstring
# macOS (with Homebrew)
brew install php-
Install Dependencies
composer install
-
Configure Environment (Optional) Copy
.env.exampleto.envand customize as needed:cp .env.example .env
See Production Deployment section for details on configuration options.
-
Start the WebSocket Server
# Start as daemon (recommended for production) php server.php start # Or run directly for development php server.php
The WebSocket server will run on
ws://localhost:8080Daemon commands:
php server.php start # Start daemon php server.php stop # Stop daemon php server.php restart # Restart daemon php server.php status # Check status php server.php start --quiet # Start with suppressed "already running" message (for cron)
-
Serve the Web Files Set up a web server pointing to the
public/directory. For development:cd public php -S localhost:3000
Visit http://localhost:3000/ to access the full walkie talkie interface.
Embed in an iframe:
<iframe
src="http://localhost:3000/embed.php?channel=1&theme=default&width=300px&height=200px"
width="300"
height="200"
frameborder="0">
</iframe>channel: Channel number (default: "1")theme: UI theme ("default", "dark", "minimal")width: Embed width (default: "100%")height: Embed height (default: "300px")
The application includes powerful command-line utilities for programmatic audio management and automated announcements.
The CLI tools enable:
- Automated Announcements: Send pre-recorded audio to channels (system alerts, notifications, scheduled messages)
- Welcome Messages: Greet users automatically when they connect or join channels
- Bot Integration: Create audio bots for announcements, time checks, or automated responses
- Testing: Inject test audio for development and quality assurance
Example audio files are provided in examples/audio/ for testing and reference.
Generate JWT tokens for authenticated users and create service accounts for automation:
# List all registered users
php cli/generate-token.php --list
# Generate token for an existing user
php cli/generate-token.php username
# Create a service account and generate token (for bots/automation)
php cli/generate-token.php --create AudioBot
php cli/generate-token.php --create NotificationSystemService Accounts:
- Purpose: Bot accounts for automated announcements and CLI tools
- No Passkey Required: Service accounts don't need WebAuthn credentials
- Same Permissions: Can send audio and join channels like regular users
- Persistent: Remains in database, generate new tokens as needed
Prerequisites:
JWT_SECRETmust be configured in.env- For regular users: Must be registered via
/login.htmlwith WebAuthn/passkey - For service accounts: Use
--createflag to create instantly
Token Information:
- Default expiration: 1 hour (configurable via
JWT_ACCESS_EXPIRATION) - Tokens are tied to specific user accounts
- Include username and user ID in payload
- Generate new tokens when expired
Example Workflow:
# Create a bot account
php cli/generate-token.php --create AudioBot
# Use the token to send announcements
php cli/walkie-cli.php send announcement.wav --channel 1 --token "eyJhbG..."
# Later, generate a new token for the same account
php cli/generate-token.php AudioBotSend pre-recorded audio files to channels for announcements or notifications:
# Send audio with screen name
php cli/walkie-cli.php send announcement.wav --channel 1 --screen-name "AudioBot"
# Send audio with JWT token
php cli/walkie-cli.php send message.wav --channel 1 --token "eyJhbGc..."
# Send raw PCM16 from stdin
cat audio.pcm | php cli/walkie-cli.php send - --channel 1 --screen-name "Bot" --sample-rate 48000
# Specify custom WebSocket URL
php cli/walkie-cli.php send msg.wav --channel 1 --screen-name "Bot" --url "ws://example.com:8080"Supported Audio Formats:
- WAV files (PCM, 16-bit, mono) - format detected automatically
- Raw PCM16 data (requires --sample-rate flag)
Options:
--channel <id>- Channel to send to (required)--token <jwt>- JWT access token for authentication--screen-name <name>- Screen name for anonymous mode--url <url>- WebSocket server URL (default: ws://localhost:8080)--sample-rate <rate>- Sample rate for raw PCM16 (default: 48000)--chunk-size <bytes>- Chunk size in bytes (default: 4096)--verbose- Show detailed progress
Configure automated welcome messages that play when users connect or join channels:
# List all welcome messages
php cli/welcome-manager.php list
# Add server welcome message (plays on connect)
php cli/welcome-manager.php add \
--name "Server Welcome" \
--audio data/audio/welcome.wav \
--trigger connect
# Add channel-specific welcome (plays on channel join)
php cli/welcome-manager.php add \
--name "Channel 1 Welcome" \
--audio data/audio/channel1.wav \
--trigger channel_join \
--channel 1
# Add message for both triggers
php cli/welcome-manager.php add \
--name "Universal Welcome" \
--audio data/audio/welcome-all.wav \
--trigger both
# Delete, enable, or disable messages
php cli/welcome-manager.php delete --id 3
php cli/welcome-manager.php disable --id 2
php cli/welcome-manager.php enable --id 2
# Test a welcome message
php cli/welcome-manager.php test --id 1 --channel 1
# Show statistics
php cli/welcome-manager.php statsSetup:
- Run database migration:
php migrations/003_add_welcome_messages.php - Add welcome messages using the CLI tool
- Restart WebSocket server:
php server.php restart
Trigger Types:
connect- Plays when user authenticates or sets screen namechannel_join- Plays when user joins a channelboth- Plays on both events
Features:
- Multiple welcome messages per trigger type
- Channel-specific or server-wide messages
- Enable/disable messages without deleting
- Playback statistics and testing tools
- Automated playback on user events
Example Audio Files:
The examples/audio/ directory includes sample files to get started:
examples/audio/
├── welcome/
│ ├── server-welcome.wav # General server greeting
│ ├── server-welcome-tones.wav # Tone-based welcome
│ ├── channel1-welcome.wav # Channel 1 specific
│ └── channel2-welcome.wav # Channel 2 specific
└── announcements/
├── notification.wav # Generic notification sound
├── test-tone-440hz.wav # A4 test tone
└── test-tone-1000hz.wav # 1kHz test tone
Copy these to data/audio/ and reference them in your welcome message configuration.
Environment Variables:
WELCOME_ENABLED=true # Enable/disable welcome messages
CLI_DEFAULT_WEBSOCKET_URL=ws://localhost:8080
CLI_DEFAULT_SAMPLE_RATE=48000
CLI_DEFAULT_CHUNK_SIZE=4096Use Cases:
- Greet new users with instructions or server rules
- Play channel-specific announcements (e.g., "Welcome to Tech Support")
- Add audio branding to your walkie talkie instance
- Provide audible feedback for successful authentication
Walkie-Talkie includes a powerful plugin system that allows you to extend functionality without modifying core code.
- Event-based Architecture: Hook into server lifecycle, connections, audio, channels, and more
- Easy Installation: Drop plugin into directory, enable in config, restart server
- Example Plugins Included: Hello World and Rate Limiter plugins demonstrate core concepts
- Configurable: Each plugin has its own configuration via plugin.json or config.php
- Secure: Review and test plugins before deployment
Example plugins are included in plugins/example-plugins/:
Hello World Plugin:
# Copy to plugins directory
cp -r plugins/example-plugins/hello-world plugins/
# Edit plugin.json and set "enabled": true
nano plugins/hello-world/plugin.json
# Restart server
php server.php restartRate Limiter Plugin (prevents transmission spam):
# Copy and enable
cp -r plugins/example-plugins/rate-limiter plugins/
nano plugins/rate-limiter/plugin.json # Set enabled: true
php server.php restartPlugins can hook into various events:
- Server:
plugin.server.init,plugin.server.shutdown - Connections:
plugin.connection.open,plugin.connection.authenticate,plugin.connection.close - Channels:
plugin.channel.join,plugin.channel.leave - Audio:
plugin.audio.transmit.start,plugin.audio.transmit.end,plugin.audio.chunk - Messages:
plugin.message.receive,plugin.message.send - Screen Names:
plugin.screen_name.validate,plugin.screen_name.generate
See the Plugin Development Guide for detailed documentation on:
- Plugin structure and manifest format
- Available hooks and their parameters
- Configuration options
- Best practices and security considerations
- API reference
Minimal plugin structure:
my-plugin/
├── plugin.json # Plugin manifest with metadata
├── MyPlugin.php # Main class implementing PluginInterface
├── config.php # Optional configuration
└── README.md # Optional documentation
# Disable plugins entirely
PLUGINS_ENABLED=false
# Custom plugins directory
PLUGINS_PATH=custom-plugins/For complete documentation, see:
- Plugin Development Guide - Full API reference and development guide
- Plugin README - Installation and usage instructions
- Example Plugins - Working examples with source code
- WebSocket Server (
src/WebSocketServer.php): Handles real-time communication - ReactPHP: Powers the WebSocket server
- Audio Streaming: Base64-encoded PCM16 audio chunks
- Message History: SQLite database with WAL mode for concurrent access
- Automatic Cleanup: Age-based and count-based message retention
- PWA Features: Service worker, manifest, offline support
- Audio Pipeline: Web Audio API → WebSocket → Broadcast to participants
- Responsive Design: Optimized for both standalone and embedded use
walkie-talkie/
├── public/
│ ├── index.php # Main application
│ ├── embed.php # Embeddable version
│ ├── manifest.json # PWA manifest
│ ├── sw.js # Service worker
│ └── assets/
│ ├── style.css # Main styles
│ ├── embed.css # Embed-specific styles
│ └── walkie-talkie.js # Core JavaScript
├── cli/
│ ├── walkie-cli.php # CLI tool for sending audio
│ ├── welcome-manager.php # CLI tool for managing welcome messages
│ ├── generate-token.php # JWT token generator for authenticated users
│ └── lib/
│ ├── AudioProcessor.php # Audio format handling
│ ├── WebSocketClient.php # WebSocket client
│ ├── AudioSender.php # Audio transmission
│ └── WelcomeManager.php # Welcome message DB operations
├── src/
│ ├── WebSocketServer.php # WebSocket server implementation
│ └── Plugins/ # Plugin system core
│ ├── PluginInterface.php
│ ├── AbstractPlugin.php
│ └── PluginManager.php
├── plugins/
│ ├── README.md # Plugin installation guide
│ ├── example-plugins/ # Example plugins (in git)
│ │ ├── hello-world/ # Simple demonstration plugin
│ │ └── rate-limiter/ # Transmission rate limiting
│ └── {your-plugin}/ # Custom plugins (gitignored)
├── data/
│ └── walkie-talkie.db # SQLite database (auto-created)
├── migrations/
│ └── 003_add_welcome_messages.php # Welcome messages migration
├── docs/
│ ├── PLUGINS.md # Plugin development guide
│ └── AUDIOMANAGER.md # CLI tools implementation plan
├── server.php # Server startup script
├── composer.json # PHP dependencies
├── .env.example # Environment configuration template
└── README.md # This file
- Authentication: Users can register/login with passkeys or use anonymous mode with a screen name
- Connection: Users connect to the WebSocket server with their identity (JWT token or screen name)
- Channel Join: Join a channel to start communicating with others on that channel
- Push-to-Talk: Hold the microphone button to start recording
- Audio Transmission: Audio is captured, encoded as PCM16, converted to base64, and sent via WebSocket
- Broadcasting: Server broadcasts audio to all channel participants with sender's screen name
- Message Storage: Complete transmissions are saved to SQLite database with user identity and retention policies
- Playback: Recipients decode and play the audio instantly, with access to message history showing who spoke
- Chrome/Edge: Full support
- Firefox: Full support
- Safari: Full support (iOS 14.3+)
- Mobile browsers: Optimized for touch interfaces
This application now supports WebAuthn/passkeys for passwordless authentication with optional anonymous mode.
-
Generate JWT Secret:
openssl rand -base64 64
-
Update your
.envfile:# WebAuthn Configuration WEBAUTHN_RP_NAME="Walkie Talkie" WEBAUTHN_RP_ID=localhost # Change to your domain in production WEBAUTHN_ORIGINS=http://localhost:3000 # Comma-delimited list of allowed origins # JWT Configuration JWT_SECRET=<your-generated-secret-here> JWT_ACCESS_EXPIRATION=3600 # 1 hour JWT_REFRESH_EXPIRATION=604800 # 7 days # Authentication Settings ANONYMOUS_MODE_ENABLED=true # Allow unauthenticated users REGISTRATION_ENABLED=true # Allow new user registration
-
Run database migration:
php migrations/001_add_authentication.php
- Passwordless Login: Use biometrics (fingerprint, face ID) or security keys
- Multi-device Support: Register multiple passkeys per account (phone, laptop, YubiKey, etc.)
- Anonymous Mode: Optional guest access with temporary screen names
- Unique Screen Names: Enforced across all users (registered + anonymous)
- JWT Tokens: Secure, stateless session management
- Automatic Refresh: Tokens refresh automatically before expiration
Registered Users:
- Visit
/login.htmlto create account or login - Use biometrics or security key for authentication
- Persistent screen name across sessions
- Manage multiple passkeys from
/passkeys.html
Anonymous Users (if enabled):
- Prompted to choose a screen name on first visit (or auto-generated if cancelled)
- Auto-generated names follow pattern: AdjectiveNoun### (e.g., "BoldEagle742")
- Screen name validated for uniqueness across all users
- Persists for browser session, lost on disconnect
# Disable anonymous mode (require login)
ANONYMOUS_MODE_ENABLED=false
# Disable new registrations (existing users only)
REGISTRATION_ENABLED=false
# Screen name rules
SCREEN_NAME_MIN_LENGTH=2
SCREEN_NAME_MAX_LENGTH=20
SCREEN_NAME_PATTERN=^[a-zA-Z0-9_-]+$- HTTPS Required: WebAuthn only works over HTTPS (except localhost)
- RP ID: Must match your domain exactly (no port, no protocol)
- For
example.com→ useWEBAUTHN_RP_ID=example.com - For
sub.example.com→ useWEBAUTHN_RP_ID=sub.example.com
- For
- Origins: Must include protocol and port (comma-delimited list for multiple origins)
- For single origin:
WEBAUTHN_ORIGINS=https://example.com - For multiple origins:
WEBAUTHN_ORIGINS=https://example.com,https://example.com:8443,https://app.example.com
- For single origin:
For detailed implementation guide, see docs/WEBAUTHN.md
- HTTPS required for microphone access and WebAuthn in production
- JWT Secret: Generate strong secret, never commit to version control
- Passkeys: Phishing-resistant, private keys never leave device
- Token Security: Access tokens in localStorage, refresh tokens in HTTP-only cookies
- Configure CORS headers for cross-origin embedding
- Rate limiting on authentication endpoints (5 attempts per 5 minutes)
- All database queries use prepared statements (SQL injection protection)
For production environments, set up the WebSocket server to run automatically:
-
Add to crontab (Linux/macOS):
crontab -e
Add these lines (update path to your installation):
# Start daemon at system reboot @reboot cd /path/to/walkie-talkie && php server.php start --quiet # Check every 5 minutes and start if not running (quiet mode suppresses "already running" spam) */5 * * * * cd /path/to/walkie-talkie && php server.php start --quiet
Note: The
--quietflag suppresses console output when the daemon is already running, preventing cron email spam while still logging towalkie-talkie.log. -
Log files:
- Daemon logs:
walkie-talkie.log - PID file:
walkie-talkie.pid
- Daemon logs:
-
Environment Configuration: Create a
.envfile to customize settings (or copy.env.example):# Server listening configuration (where the server binds) WEBSOCKET_HOST=0.0.0.0 # IP address the server listens on WEBSOCKET_PORT=8080 # Port the server listens on # Client connection URL (what browsers use to connect) WEBSOCKET_URL=ws://localhost:8080 # Message History Configuration MESSAGE_HISTORY_MAX_COUNT=10 # Maximum messages per channel MESSAGE_HISTORY_MAX_AGE=300 # Maximum age in seconds (5 minutes) # Optional settings DEBUG=false
Important: The distinction between server and client settings:
WEBSOCKET_HOSTandWEBSOCKET_PORT: Define where the WebSocket server listens for connections- Use
0.0.0.0to listen on all network interfaces (recommended for production) - Use
127.0.0.1to listen only on localhost (more secure for development)
- Use
WEBSOCKET_URL: Defines the actual URL that client browsers use to connect to the server- This is embedded in the web interface and used by JavaScript to establish connections
- Can differ from the server settings when using proxies, load balancers, or public domains
- Use
ws://for development orwss://for secure production connections - Examples:
- Local development:
ws://localhost:8080 - Behind proxy:
wss://your-domain.com/ws - Public IP:
ws://203.0.113.45:8080
- Local development:
Message History Configuration:
MESSAGE_HISTORY_MAX_COUNT: Maximum number of messages to keep per channel (default: 10)MESSAGE_HISTORY_MAX_AGE: Maximum message age in seconds (default: 300 = 5 minutes)- Messages are deleted when EITHER limit is exceeded
- Examples:
- Short-term:
MESSAGE_HISTORY_MAX_COUNT=5andMESSAGE_HISTORY_MAX_AGE=60(1 minute) - Long-term:
MESSAGE_HISTORY_MAX_COUNT=100andMESSAGE_HISTORY_MAX_AGE=86400(24 hours)
- Short-term:
- Database stored in
data/walkie-talkie.db(auto-created)
You can inject custom HTML into the header and footer of all pages:
-
Create template files:
# Copy example templates cp templates/header.php.example templates/header.php cp templates/footer.php.example templates/footer.php -
Customize the templates:
templates/header.php- Included right after</head>(top of body)templates/footer.php- Included right before</body>(bottom of body)
-
Example use cases:
- Analytics (Google Analytics, Plausible, etc.)
- Chat widgets (Intercom, Drift, etc.)
- Custom banners or notifications
- Additional tracking scripts
- Custom CSS or JavaScript
Note: These files are excluded from git, so they won't be overwritten during updates.
- Update the WebSocket server to handle dynamic channels
- Modify the frontend to allow channel selection
- Update the embed.php to accept channel parameters
- User authentication: Add login system
- Channel management: Create/join/leave channels
- Per-channel configuration: Different retention settings per channel
- Video support: Add webcam functionality
- Export functionality: Download message history
Microphone access denied: Ensure HTTPS in production, allow microphone permissions
WebSocket connection failed: Check if server.php is running on port 8080
No audio playback: Verify browser autoplay policies and volume settings
Embed not loading: Check CORS headers and iframe permissions
Message history not saving:
- Ensure
pdo_sqlitePHP extension is installed:php -m | grep pdo_sqlite - Ensure
data/directory exists and is writable (chmod 755 or 777) - Check server logs:
tail -f walkie-talkie.log
Database locked errors: WAL mode should prevent this, but check file permissions on data/ directory
Message history panel not visible on live demo: Clear browser cache or perform hard refresh (Ctrl+Shift+R / Cmd+Shift+R)
Dual licensed. AGPL-3.0 License for Open Source. Commercial/Non Open Source usage requires a separate licensing agreement.
See LICENSE.md for details.