Player¶
UI ยท v0.1.0
Play what a server speaks, for any server speaking the synthesizer contract. Headless playback with a jitter buffer, instant interruption and playback marks back to the server, plus a React player.
| Install | copit add @chanx-kit-ui/player |
| Or with shadcn | npx shadcn add https://huynguyengl99.github.io/chanx-kit/r/player.json |
| Contract | synthesizer@1 |
| Works with | audio-stream-out, deepgram, elevenlabs, fake-voice, openai |
| Requires UI kits | audio |
| Frameworks | react (the core is framework-free) |
| npm packages | @chanx-js/client@^0.1.2 |
| Source | ui/player |
Play what a server speaks, for any server speaking synthesizer@1:
fake-voice while you build,
elevenlabs (or any provider kit's tts part) in production.
React¶
import { Player, usePlayer } from '@/chanx-kit/player/react';
import '@/chanx-kit/player/player.css';
import { voice } from '@/generated';
export function Speech({ session }: { session: string }) {
const player = usePlayer(voice, voice.topics.synthesizerTopic.with({ session }));
return (
<>
<Player {...player} onUnlock={player.unlock} onStop={player.stop} />
<button onClick={() => player.speak('Hello there.')}>Say hello</button>
</>
);
}
The server can speak on its own too (SynthesizerTopic.speak(session, text) from an
agent or a worker); every player on the session plays it.
What the playback does¶
- Jitter buffer: an AudioWorklet waits for
prebufferMs(150 ms) of audio before playing, and again whenever it runs dry mid-utterance; the end of an utterance plays out regardless. - Resampling from the utterance's rate to the device's.
- Replay-safe: a tab joining mid-utterance is replayed it; chunks already played
(by
index) are skipped. - Interruption:
clearfrom the server empties the buffer at once.stop()asks the server for that, which clears every listener. - Playback marks:
playback_markeverymarkEveryMs(250 ms) and at the end or on a clear, so the server knows how much was actually heard. - Autoplay: browsers hold audio until a user gesture.
needsUnlocksays so; callunlock()from a click (the<Player>shows an "Enable audio" button).
The worklet is inline JS loaded from a Blob; with a CSP that forbids blob:, serve
PLAYBACK_WORKLET as a file and pass workletUrl.
Test on real devices
Autoplay rules and audio output differ by browser and platform (iOS Safari first).