Two reports, one root: the platform's speech engine.
"Loud cracking at the end of every sentence" is the browser/OS TTS popping
between its internal sentence chunks (an idle cancel makes it worse). Hygiene
for that path: never cancel when nothing is pending, normalise the spoken text
(control characters, ellipses, dashes), and a 140ms beat between sections so
speech never runs into the engine's tail.
The real fix is a second voice source that does not use the platform engine:
- lib/providers carries TTS profiles (OpenAI gpt-4o-mini-tts, Groq
playai-tts, any OpenAI-compatible /audio/speech behind the custom provider),
male voices first.
- /api/tts proxies them with the same guards and the key still in headers.
- the picker gains System / Natural, with the provider's voice list.
- lib/podcast-audio plays one clip per section through a hidden <audio>
element, measures the clip's real duration and gives each word a slice of it
proportional to its spoken length - the highlight follows the audio exactly,
and pause/seek/chapters become real audio operations. Clips cache per
session, the next section generates while the current plays, and a voice
change disposes the element and its blobs. Speed applies live (setRate)
without dropping the cache.
podcast-test stubs /api/tts with a real silent WAV and pins the path: the
voice list, the request body, exactly one playing element, and the highlight
riding currentTime. 43 checks. tests/lib.mjs launches Chromium with the
autoplay policy relaxed (a real device has the listener's gesture).
Version 0.7.6, SW v98.