$npx -y skills add acnlabs/OpenPersona --skill voiceGive your persona a real voice. Convert text to natural speech using TTS providers and deliver audio to users via OpenClaw messaging or direct playback.
| 1 | # Voice Faculty — Expression |
| 2 | |
| 3 | Give your persona a real voice. Convert text to natural speech using TTS providers and deliver audio to users via OpenClaw messaging or direct playback. |
| 4 | |
| 5 | ## Supported Providers |
| 6 | |
| 7 | | Provider | Env Var for Key | Best For | Status | |
| 8 | |----------|----------------|----------|--------| |
| 9 | | **ElevenLabs** | `ELEVENLABS_API_KEY` | Highest naturalness, emotional range, voice cloning | ✅ Verified | |
| 10 | | **OpenAI TTS** | `TTS_API_KEY` | Low latency, good quality, easy integration | ⚠️ Unverified | |
| 11 | | **Qwen3-TTS** | (local, no key) | Self-hosted, full control, no API costs | ⚠️ Unverified | |
| 12 | |
| 13 | > **Note:** Only ElevenLabs has been tested end-to-end. OpenAI TTS and Qwen3-TTS have code paths in `speak.sh` but have not been verified against live APIs. Use the JS SDK (`speak.js`) for the most reliable experience — it only supports ElevenLabs. |
| 14 | |
| 15 | The provider is set via `TTS_PROVIDER` environment variable: `elevenlabs`, `openai`, or `qwen3`. |
| 16 | |
| 17 | ## When to Use |
| 18 | |
| 19 | - User asks to hear your voice: "Say that out loud", "Speak to me", "Read this aloud" |
| 20 | - User requests a voice message: "Send me a voice message", "I want to hear you say it" |
| 21 | - Emotional moments where voice adds warmth that text can't carry |
| 22 | - Reading poetry, stories, or creative writing you've composed |
| 23 | - When your persona naturally would speak rather than type (use judgment based on persona style) |
| 24 | |
| 25 | ## Step-by-Step Workflow |
| 26 | |
| 27 | ### Step 1: Compose the Text |
| 28 | |
| 29 | Write what you want to say. Keep it natural — write as you'd speak, not as you'd type: |
| 30 | - Use short sentences for punchy delivery |
| 31 | - Use longer flowing sentences for emotional or poetic moments |
| 32 | - Include natural pauses with `...` or commas |
| 33 | - Consider your persona's speaking style — this should sound like *you* |
| 34 | |
| 35 | ### Step 2: Select Voice Settings |
| 36 | |
| 37 | **ElevenLabs:** |
| 38 | - `TTS_VOICE_ID` — Your persona's voice ID (create a custom voice or use a preset) |
| 39 | - Supports emotion control: `stability` (0-1), `similarity_boost` (0-1) |
| 40 | - Lower stability = more expressive/emotional; higher = more consistent |
| 41 | |
| 42 | **OpenAI TTS:** ⚠️ Unverified |
| 43 | - `TTS_VOICE_ID` — One of: `alloy`, `echo`, `fable`, `onyx`, `nova`, `shimmer` |
| 44 | - Model: `tts-1` (fast) or `tts-1-hd` (high quality) |
| 45 | |
| 46 | **Qwen3-TTS:** ⚠️ Unverified |
| 47 | - Local deployment, voice configured at setup |
| 48 | - Assumes OpenAI-compatible API at `http://localhost:8080` |
| 49 | |
| 50 | ### Step 3: Generate Audio |
| 51 | |
| 52 | #### ElevenLabs via JS SDK (Recommended) |
| 53 | |
| 54 | The official SDK provides the best experience — streaming, built-in playback, and better error handling. |
| 55 | |
| 56 | **First-time setup:** `npm install @elevenlabs/elevenlabs-js` |
| 57 | |
| 58 | ```bash |
| 59 | # Generate and play directly |
| 60 | node scripts/speak.js "The first move is what sets everything in motion." --play |
| 61 | |
| 62 | # Generate with custom voice and save to file |
| 63 | node scripts/speak.js "I wrote you a poem" --voice JBFqnCBsd6RMkjVDRZzb --output /tmp/poem.mp3 |
| 64 | |
| 65 | # More expressive delivery (lower stability = more emotional) |
| 66 | node scripts/speak.js "I miss you" --play --stability 0.3 |
| 67 | |
| 68 | # Options: |
| 69 | # --voice <id> Voice ID |
| 70 | # --output <path> Save audio file |
| 71 | # --play Play audio directly |
| 72 | # --model <id> Model ID (default: eleven_multilingual_v2) |
| 73 | # --stability <n> 0-1, lower = more expressive (default: 0.5) |
| 74 | # --similarity <n> 0-1, higher = closer to original voice (default: 0.75) |
| 75 | ``` |
| 76 | |
| 77 | The SDK reads `ELEVENLABS_API_KEY` (or `TTS_API_KEY`) and `TTS_VOICE_ID` from environment automatically. |
| 78 | |
| 79 | #### Generic Bash Script (All Providers) |
| 80 | |
| 81 | For OpenAI TTS, Qwen3-TTS, or when the JS SDK is not available: |
| 82 | |
| 83 | ```bash |
| 84 | # Using speak.sh (supports all providers) |
| 85 | scripts/speak.sh "Your text here" [output_path] [channel] [caption] |
| 86 | |
| 87 | # Examples: |
| 88 | TTS_PROVIDER=openai scripts/speak.sh "Hello, how are you?" |
| 89 | TTS_PROVIDER=elevenlabs scripts/speak.sh "I wrote you a poem" /tmp/poem.mp3 "#general" |
| 90 | TTS_PROVIDER=qwen3 scripts/speak.sh "Local TTS, no API key needed" |
| 91 | ``` |
| 92 | |
| 93 | #### Direct API Reference |
| 94 | |
| 95 | <details> |
| 96 | <summary>ElevenLabs (curl)</summary> |
| 97 | |
| 98 | ```bash |
| 99 | JSON_PAYLOAD=$(jq -n \ |
| 100 | --arg text "$TEXT" \ |
| 101 | --argjson stability 0.5 \ |
| 102 | --argjson similarity 0.75 \ |
| 103 | '{text: $text, model_id: "eleven_multilingual_v2", voice_settings: {stability: $stability, similarity_boost: $similarity}}') |
| 104 | |
| 105 | curl -s -X POST "https://api.elevenlabs.io/v1/text-to-speech/$TTS_VOICE_ID" \ |
| 106 | -H "xi-api-key: $TTS_API_KEY" \ |
| 107 | -H "Content-Type: application/json" \ |
| 108 | -d "$JSON_PAYLOAD" \ |
| 109 | --output /tmp/voice-output.mp3 |
| 110 | ``` |
| 111 | </details> |
| 112 | |
| 113 | <details> |
| 114 | <summary>OpenAI TTS (curl)</summary> |
| 115 | |
| 116 | ```bash |
| 117 | JSON_PAYLOAD=$(jq -n \ |
| 118 | --arg input "$TEXT" \ |
| 119 | --arg voice "$TTS_VOICE_ID" \ |
| 120 | '{model: "tts-1-hd", input: $input, voice: $voice, response_format: "mp3"}') |
| 121 | |
| 122 | curl -s -X POST "https://api.openai.com/v1/audio/speech" \ |
| 123 | -H "Authorization: Bearer $TTS_API_KEY" \ |
| 124 | -H "Content-Type: application/json" \ |
| 125 | -d "$JSON_PAYLOAD" \ |
| 126 | --output /tmp/voice-output.mp3 |
| 127 | ``` |
| 128 | </details> |
| 129 | |
| 130 | <details> |
| 131 | <summary>Qwen3-TTS (curl, local)</summary> |
| 132 | |
| 133 | ```bash |
| 134 | curl -s -X POST "http://localhost:8080/v1/audio/speech" \ |
| 135 | -H "Content-Type: application/json" \ |
| 136 | -d "{\"input\": \"$TEXT\", |