A fully-functional blackjack game demonstrating how to build sophisticated SignalWire AI Agents with integrated web serving. This project showcases a single-service architecture where the AI agent serves both the SWML API and the web client, with real-time communication via WebRTC video calling and event-driven architecture.
Visit https://blackjack.signalwire.me to play!
This demonstration application illustrates best practices for building SignalWire AI Agents that:
- Serve both API and web client from a single process
- Maintain complex stateless game logic
- Communicate with web frontends via SWML events
- Manage conversation flow through structured steps
- Integrate WebRTC video/audio for immersive experiences
- Handle real-time user interactions and state synchronization
- Provide public web access while protecting API endpoints
The blackjack game serves as a practical example of these concepts, featuring a professional AI dealer that manages gameplay, maintains state, and provides real-time UI updates through SignalWire's platform.
- AI-Powered Dealer: Professional dealer using ElevenLabs' Adam voice
- Integrated Web Serving: Single process serves both API and web client
- Standard Casino Rules: Dealer hits on 16, stands on 17
- Complete Game Mechanics:
- Betting system with starting chips (1000)
- Hit, Stand, and Double Down actions
- Automatic dealer play following casino rules
- Blackjack bonus payout (3:2)
- Interactive Web Interface: Real-time card display with animations
- Video Call Integration: Face-to-face gaming via SignalWire WebRTC
- Stateless Architecture: Centralized game state management with proper flow control
- Step-Based Conversation Flow: Structured gameplay with betting → playing → hand_complete steps
- Social Media Ready: Open Graph meta tags for rich social sharing
- Authentication: Public web access with protected API endpoints
- Event Logging: Optional debug log for monitoring game events
blackjack/
├── bot/ # Bot implementation
│ ├── sigmond_blackjack.py # AI dealer with integrated web server
│ └── bot.sh # Control script for starting/stopping
├── web/ # Web interface and media files
│ ├── client/ # Frontend application
│ │ ├── index.html # Main UI with Open Graph meta tags
│ │ ├── app.js # JavaScript game logic
│ │ ├── signalwire.js # SignalWire SDK
│ │ └── favicon.svg # Blackjack-themed favicon
│ ├── card_images/ # Playing card images
│ ├── og-image.svg # Social media preview image
│ ├── sigmond_bj_idle.mp4 # Dealer idle video
│ ├── sigmond_bj_talking.mp4 # Dealer talking video
│ └── casino.mp3 # Background music
├── Procfile # Heroku/Dokku deployment config
├── requirements.txt # Python dependencies
├── runtime.txt # Python version specification
└── CLAUDE.md # Developer documentation
Optional:
BLACKJACK_WEB_ROOT
: URL where media files are hosted (defaults tohttp://localhost:PORT
)PORT
: Port to run the service on (default: 5000)SWML_DEV_USERNAME
: Basic auth username for API (auto-generated if not set)SWML_DEV_PASSWORD
: Basic auth password for API (auto-generated if not set)
To run the bot with HTTPS enabled, set the following environment variables:
export SWML_SSL_ENABLED=true
export SWML_SSL_CERT_PATH=/path/to/cert.pem
export SWML_SSL_KEY_PATH=/path/to/key.pem
export SWML_DOMAIN=yourdomain.com
The SignalWire Agents SDK provides comprehensive security features including:
- SSL/TLS encryption
- Basic authentication (enabled by default)
- HSTS headers
- CORS configuration
- Rate limiting
- Request size limits
For complete security configuration options, see the SignalWire Agents Security Documentation.
-
Install dependencies:
pip install -r requirements.txt
-
Run the integrated server:
cd bot python sigmond_blackjack.py # Runs on port 5000 by default
-
Access the application:
- Web client:
http://localhost:5000/
- API endpoint:
http://localhost:5000/blackjack
(requires auth)
- Web client:
cd bot
./bot.sh start # Start the dealer on port 5000
./bot.sh restart # Restart the dealer
./bot.sh status # Check if dealer is running
./bot.sh logs # View logs
./bot.sh stop # Stop the dealer
python sigmond_blackjack.py --port 8080
export SWML_SSL_ENABLED=true
export SWML_SSL_CERT_PATH=/path/to/cert.pem
export SWML_SSL_KEY_PATH=/path/to/key.pem
export SWML_DOMAIN=yourdomain.com
python sigmond_blackjack.py
The application is configured for easy deployment to Heroku or Dokku:
# For Heroku
git push heroku main
# For Dokku
git push dokku main
The Procfile automatically configures the service to run on the platform's assigned PORT.
-
Update the SignalWire token in
web/client/app.js
:const STATIC_TOKEN = 'your-signalwire-token-here';
-
Update the destination for your SignalWire routing:
const DESTINATION = '/public/your-agent-name';
- Connect: Click "Connect to Dealer" to initiate a video call
- Place Bet: Tell the dealer how much you want to bet (minimum 10 chips)
- Initial Deal: Receive two cards, dealer shows one card
- Player Actions:
- Hit: Say "hit" to take another card
- Stand: Say "stand" to keep your current hand
- Double Down: Say "double down" to double your bet and receive exactly one more card
- Dealer Turn: Dealer reveals hole card and draws according to rules
- Resolution: Hand completes, winner determined, cards remain visible
- New Hand: Say "yes" when asked if you want to play another hand
The game uses a structured step-based conversation flow:
-
betting: Starting step where players place their bets
- Functions available:
place_bet
- Transitions to:
playing
(after cards dealt)
- Functions available:
-
playing: Active gameplay where players make decisions
- Functions available:
hit
,stand
,double_down
- Transitions to:
hand_complete
(when hand ends)
- Functions available:
-
hand_complete: Review results with cards still visible
- Functions available:
new_hand
- Transitions to:
betting
(when new hand requested)
- Functions available:
- Stateless Architecture: Each function call receives complete game state
- State Persistence: SignalWire mirrors back
global_data
between calls - Centralized Logic: All game decisions made by Python, AI only narrates
- Automatic Resolution: Hands resolve immediately when complete (bust, 21, or stand)
- Goal: Get as close to 21 as possible without going over
- Card Values:
- Number cards (2-10): Face value
- Face cards (J, Q, K): 10 points
- Aces: 1 or 11 points (automatically optimized)
- Dealer Rules:
- Must hit on 16 or below
- Must stand on 17 or above
- Payouts:
- Regular win: 1:1 (double your bet)
- Blackjack (21 with first 2 cards): 3:2
- Push (tie): Bet returned
- Port: Configure with
--port
flag (default: 3010) - Voice: Uses ElevenLabs Adam voice
- Starting Chips: 1000
- Minimum Bet: 10 chips
- Media Files: Configured via
BLACKJACK_WEB_ROOT
environment variable
- SignalWire Token: Update
STATIC_TOKEN
in app.js - Destination: Update
DESTINATION
for SignalWire routing - Card Images: Uses relative paths (../card_images from client directory)
The application now uses a single-process architecture where the SignalWire agent serves both the API and web client:
- Single Port: Everything runs on one port (default 5000)
- Public Access: Web client and static files are publicly accessible
- Protected API: Only
/blackjack
endpoints require authentication - Simplified Deployment: No need for separate web server process
-
Public Routes (no auth required):
/
- Web client interface/app.js
,/signalwire.js
- JavaScript files/card_images/*
- Card images/favicon.svg
- Favicon/og-image.png
- Social media preview- Media files (videos, audio)
-
Protected Routes (Basic Auth required):
/blackjack
- SWML endpoint/blackjack/swaig
- SWAIG functions/blackjack/post_prompt
- Post-prompt webhook
- Open Graph meta tags for rich previews
- Custom preview image with game branding
- Optimized for Facebook, Twitter, LinkedIn, Discord
This application demonstrates several important patterns for SignalWire agent development:
-
Stateless Architecture Pattern
# State retrieved from global_data on each call game_state, global_data = get_game_state(raw_data) # State persisted back after modifications global_data['game_state'] = game_state global_data['current_chips'] = game_state['player_chips'] result.update_global_data(global_data)
-
UI Event Communication Pattern
# Send real-time updates to web client result.swml_user_event({ "type": "cards_dealt", "player_hand": game_state["player_hand"], "dealer_hand": [game_state["dealer_hand"][0], None], "player_score": game_state["player_score"] })
-
Conversation Flow Pattern
# Define structured conversation steps default_context.add_step("betting") .add_bullets("Betting Process", [ "The player has ${global_data.current_chips} chips", "Ask how much they'd like to bet" ]) .set_functions(["place_bet"]) .set_valid_steps(["playing"])
-
SignalWire Agents SDK Features:
swml_user_event()
: Send UI update events to clientswml_change_step()
: Navigate between conversation stepsupdate_global_data()
: Persist state between function calls
-
Game Functions:
place_bet
: Accepts bet, deals cards, checks for blackjackhit
: Draws card, checks for bust or 21stand
: Plays dealer hand, resolves gamedouble_down
: Doubles bet, draws one card, resolvesnew_hand
: Resets table, transitions to betting
-
UI Events:
clear_table
: Resets UI before new handbet_placed
: Updates chip displaycards_dealt
: Shows initial cardsplayer_hit
: Adds card to player handdealer_play
: Reveals dealer cardshand_resolved
: Shows winner and updates chipsgame_reset
: Clears table for new hand
Start → betting (place_bet) → playing (hit/stand/double_down) → hand_complete (new_hand) → betting
Each step has:
- Clear instructions for the AI
- Available functions
- Valid next steps
- Completion criteria
cd bot
swaig-test sigmond_blackjack.py --list-tools
swaig-test sigmond_blackjack.py --exec place_bet --amount 50
- Start the server:
python sigmond_blackjack.py
- Open browser to
http://localhost:5000
- Check API endpoint:
curl -u username:password http://localhost:5000/blackjack
-
Port Already in Use:
- Default port is 5000, use
--port
flag to specify another - Check if another process is using the port:
lsof -i :5000
- Default port is 5000, use
-
Connection Issues:
- Verify SignalWire token is correct in
app.js
- Check bot is running (
./bot.sh status
) - Ensure the destination matches your agent configuration
- Verify SignalWire token is correct in
-
404 Errors on Static Files:
- Ensure you're running from the
bot
directory - Check that web files exist in
../web/
relative to bot directory
- Ensure you're running from the
-
SWAIG Functions Not Working:
- Check browser console for 404 errors on
/blackjack/swaig
- Verify Basic Auth credentials are being sent
- Check agent logs for authentication errors
- Check browser console for 404 errors on
-
Game State Issues:
- Cards not displaying: Check browser console for WebSocket errors
- State not persisting: Verify
update_global_data
is called - Wrong URLs generated: Check
_build_webhook_url
override is working
- All game logic handled server-side for security
- Card deck reshuffled when less than 15 cards remain
- UI updates triggered by SWML user events
- Game state maintained by bot, client is display-only
- Step transitions control conversation flow
- Cards remain visible in
hand_complete
for result review
This project demonstrates key concepts for building SignalWire AI agents with web integration:
- All game state persisted via
update_global_data()
- State passed between function calls in
global_data
- No reliance on instance variables or session state
- Real-time UI updates via
swml_user_event()
- WebRTC video/audio integration with SignalWire Fabric
- Event-driven architecture for responsive gameplay
- Step-based conversation management
- Context-aware function availability
- Automatic transitions based on game state
- Media files served from configurable web root
- Flexible deployment options
- Security via basic authentication
This codebase serves as a practical example of:
- Building stateless AI agents with SignalWire
- Integrating WebRTC video calling with game logic
- Managing complex conversation flows
- Synchronizing backend state with frontend UI
- Handling real-time user events and responses
For more information on SignalWire technologies:
- SignalWire Agents SDK (GitHub) - Python SDK for building AI agents
- SignalWire AI Documentation - AI Agent guides and tutorials
- SWML Documentation - SignalWire Markup Language reference
- Browser SDK Documentation - JavaScript SDK for WebRTC