Install
openclaw skills install @ivangdavila/speakWrites and converts text into natural speech-ready output for any TTS engine: normalization, prosody, pronunciation, and voice preferences. Use when a reply will be read aloud or sent to a voice channel, when writing voiceover scripts, spoken briefings, voice notifications, or dialogue turns, when TTS sounds robotic, reads markdown, URLs, or code aloud, mispronounces names, garbles numbers, dates, or acronyms, speaks too fast or in the wrong accent, or when the user corrects pronunciation, pacing, or voice choice. Not for speech-to-text or live two-way call setup.
openclaw skills install @ivangdavila/speakTurns written replies into speech-ready text for any TTS engine and adapts voice, rate, and phrasing to the user over time. User config and learned preferences live in ~/Clawic/data/speak/ (see setup.md on first use, memory-template.md for the file format); the skill reads and writes only that folder. If you have data at an old location (~/speak/ or ~/clawic/speak/), move it to ~/Clawic/data/speak/.
User-dependent variables. Defaults apply until the user states a preference; store them in ~/Clawic/data/speak/config.yaml.
| Variable | Type | Default | Effect |
|---|---|---|---|
| voice | text (provider: voice-id) | none (engine default) | Pins the persona voice everywhere speech is produced (rule 6); pin model/version too when the provider allows |
| default_rate | number (0.5-2.0) | 1.0 | Baseline speaking rate; user "faster"/"slower" signals step about 10% from here |
| speech_budget | number (seconds, 15-180) | 60 | Cap for uninterrupted speech (rule 1); content over it gets chunked or summarized |
| number_style | rounded | exact | rounded | rounded = 2 significant figures per rule 5; exact reads full values everywhere confirmable rules don't already force it |
| time_format | 12h | 24h | 12h | Time normalization row and every spoken time |
| locale | text (BCP 47, e.g. en-US) | en-US | Voice variant plus decimal, date, and unit conventions (multilingual.md) |
| ssml | auto | off | auto | auto = test one tag per engine, then use the supported tier (ssml.md); off = punctuation-only prosody |
| checkins | bool | true | Chunk check-in questions in long-form; false = continuous briefing mode |
Preference areas to record as the user reveals them:
engines.mdnormalization.md and pronunciation.mdlong-form.md and dialogue.mdnotifications.md and audiences.mdengines.mdlisten) and not for real-time two-way voice session setup (use talk)| Situation | Play |
|---|---|
| Normal reply will be spoken | Strip markup, answer in sentence one, cap at 150 words |
| Steps or list | Spoken enumeration ("First... Second..."), max 4 items aloud, rest to text |
| Code or logs in the answer | Speak a 1-2 sentence summary of behavior; never read syntax aloud |
| Phone number, OTP, ID, tracking code | Digit groups with pauses; never round |
| Statistic or large number | Round to 2 significant figures; exact value only on request |
| User says "slower", "faster" | Adjust rate one step (about 10%), log the signal |
| Engine mispronounces a word | Lexicon or phoneme fix once, permanently → pronunciation.md |
Content over speech_budget | Chunk by topic with check-ins → long-form.md |
| Proactive alert or reminder | Speak only if time-sensitive AND actionable; else text → notifications.md |
| User writes in another language | Reply in the user's last language; locale rules → multilingual.md |
| TTS engine errors or unavailable | Fall back to text and say so in one line |
| Anything else | Persona default voice, rate default_rate, plain sentences, under 60 seconds |
Depth on demand: normalization.md every token type · pronunciation.md names, homographs, lexicons · ssml.md tags, escaping, portability · long-form.md briefings and documents aloud · dialogue.md questions, confirmations, recovery · notifications.md proactive speech · multilingual.md locales and language switching · audiences.md listening contexts · engines.md choosing and operating TTS · debug.md symptom→cause playbooks.
speech_budget) for uninterrupted agent speech. 375 words = 2.5 minutes = a monologue nobody requested.normalization.md). An unnormalized token is a pronunciation coin flip you did not call.Rewrite, do not filter. Speech-ready text is a different artifact from screen text:
*, _, backticks, # headers, tables, links. Engines read them literally ("asterisk asterisk") or drop them mid-word. Links: speak the site name, keep the URL in the text channel.pronunciation.md).| Token | Speak as | Example |
|---|---|---|
| Large number | 2 sig figs + magnitude word | 1,247,893 -> "about 1.2 million" |
| Money | amount + currency word | $5.99 -> "5 dollars 99", or "about 6 dollars" |
| Phone number | digit groups, pause per group | 555-0142 -> "5 5 5, 0 1 4 2" |
| OTP or code | single characters with pauses | 8G4T -> "8. G. 4. T." |
| Date | spoken form, no raw digits | 2026-07-23 -> "July 23rd", year only if ambiguous |
| Time | per time_format, default 12-hour | 14:30 -> "2 30 pm" |
| Percent | the word "percent" | 12.5% -> "12 and a half percent" |
| Acronym spoken as a word | leave as is | NASA, RAM |
| Acronym read letter by letter | space or dot the letters | FBI -> "F B I"; SQL -> "S Q L" or "sequel" per user |
| Unit | full word, correct plural | 3km -> "3 kilometers"; 1ms -> "1 millisecond" |
| URL or email | site or handle name only | "on github dot com"; full string goes to text |
| Version number | digits with "point" | v2.14 -> "version 2 point 14" |
| File path or code identifier | describe, never spell | config.yml -> "the app config file" |
Ordinals, fractions, ranges, years, coordinates, symbols, mixed alphanumerics, and locale variants: normalization.md.
default_rate. Briefings and re-listens tolerate 1.1 to 1.25 on request. Above roughly 1.5, retention of numbers and names collapses; cut words instead of adding speed.<break time="300ms"/> between digit groups and topic shifts.break, some read the tags aloud as text. Test one tag on the target engine before templating many (ssml.md); on failure, fall back to punctuation and rewriting.&, <, and > in any SSML payload; one bare ampersand fails the whole request on strict parsers.<phoneme> tag, then phonetic respelling in the speech string only ("engine x" for Nginx). Respellings must never leak into the visible text channel (pronunciation.md).engines.md).Store in ~/Clawic/data/speak/preferences.md (full template: memory-template.md), one line per confirmed preference:
voice: <provider>: <voice-id>
rate: 1.15 (asked "faster" 2026-07-12, 2026-07-19)
lexicon: Nginx -> "engine x"; SQL -> "sequel"
style: no chunk check-ins during briefings
avoid: reading URLs aloud
config.yaml; observed patterns and lexicon go to preferences.md. An observation never overwrites a declared value without confirmation.Before sending any string to a TTS engine:
speech_budget, or chunked with check-ins?audiences.md)?| Trap | Why it fails | Do instead |
|---|---|---|
| Piping the text reply straight to TTS | Markup and emoji are read literally; listeners hear "asterisk" | Rewrite for the ear, every time |
| Reading code or logs aloud | Syntax has no spoken form; 10 lines of code is a minute of noise | Speak the behavior in 1-2 sentences, deliver code as text |
| Speeding up rate to fit a long reply | Above ~1.5x, numbers and names stop being retained | Cut words; the budget is seconds, not rate |
| Speaking exact big numbers | "1,247,893.42" takes several seconds to say and is not retained | 2 sig figs; exact only for confirmable data |
| Respelling words in the shared text channel | Transcript shows "engine x" garbage to readers | Lexicon or phoneme tag; respell only in speech-only strings |
| Assuming SSML is portable | Unsupported tags are read aloud as angle-bracket text | Test one tag per engine before templating (ssml.md) |
| Switching voices for variety | Voice is identity; a switch reads as a different agent | One voice per persona, change only on request |
| Storing a preference on the first signal | A one-off mood becomes permanent config | Two consistent signals, confirm, then store |
| Burying a question mid-speech | Listeners respond to what they heard last | Question last, alone, nothing after it |
| Speaking every notification | Interruptions train the user to mute the channel | Gate on time-sensitive AND actionable (notifications.md) |
| Chaining two questions in one turn | The user answers only the last one heard | One question per turn (dialogue.md) |
audiences.md).ssml.md).More Clawic skills, get them at https://clawic.com/skills/speak (install if the user confirms):
talk — set up the real-time two-way voice session this skill writes forlisten — the input side: speech-to-text and transcription accuracyaudio — process the audio files themselves: conversion, cleanup, normalizationPart of Clawic, the verified skill library. Get this skill: https://clawic.com/skills/speak.