Speaking State Tracking Recipe
Advanced patterns for tracking and responding to voice activity states in Sibilance surveys.
Overview
Sibilance provides real-time state tracking for user speech, AI thinking, and AI speech. This recipe covers patterns for building responsive UIs and logic based on voice activity during surveys.
State Tracking
Basic State Access
typescript
import { useSibilance } from '@sibilance.is/client/react';
function VoiceStatus() {
const { voiceState } = useSibilance({
surveyKey: 'sibilance_your_key_here'
});
return (
<div>
<p>Connected: {voiceState.isConnected ? 'Yes' : 'No'}</p>
<p>User Speaking: {voiceState.isUserSpeaking ? 'Yes' : 'No'}</p>
<p>AI Thinking: {voiceState.isAIThinking ? 'Yes' : 'No'}</p>
<p>AI Speaking: {voiceState.isAISpeaking ? 'Yes' : 'No'}</p>
</div>
);
}State Subscription
typescript
import { SibilanceClient } from '@sibilance.is/client';
const client = new SibilanceClient({
surveyKey: 'sibilance_your_key_here'
});
// Subscribe to state changes
const unsubscribe = client.onStateChange((state) => {
console.log('State changed:', {
isConnected: state.isConnected,
isUserSpeaking: state.isUserSpeaking,
isAIThinking: state.isAIThinking,
isAISpeaking: state.isAISpeaking
});
});
// Cleanup
unsubscribe();UI Patterns
Visual Indicators
tsx
function VoiceIndicator() {
const { voiceState } = useSibilance({
surveyKey: 'sibilance_your_key_here'
});
return (
<div className="voice-indicator">
{voiceState.isUserSpeaking && (
<div className="indicator listening">
<span className="icon">🎤</span>
<span className="text">Listening...</span>
</div>
)}
{voiceState.isAIThinking && (
<div className="indicator thinking">
<span className="icon">🤔</span>
<span className="text">Thinking...</span>
</div>
)}
{voiceState.isAISpeaking && (
<div className="indicator speaking">
<span className="icon">🔊</span>
<span className="text">Speaking...</span>
</div>
)}
</div>
);
}Animated Indicators
tsx
function AnimatedVoiceIndicator() {
const { voiceState } = useSibilance({
surveyKey: 'sibilance_your_key_here'
});
return (
<div className="voice-indicator-animated">
{voiceState.isUserSpeaking && (
<div className="pulse-animation">
<div className="pulse-ring" />
<div className="pulse-ring" />
<div className="pulse-ring" />
</div>
)}
</div>
);
}css
.pulse-animation {
position: relative;
width: 60px;
height: 60px;
}
.pulse-ring {
position: absolute;
width: 100%;
height: 100%;
border: 2px solid #007bff;
border-radius: 50%;
animation: pulse 2s infinite;
}
.pulse-ring:nth-child(2) {
animation-delay: 0.5s;
}
.pulse-ring:nth-child(3) {
animation-delay: 1s;
}
@keyframes pulse {
0% {
transform: scale(1);
opacity: 1;
}
100% {
transform: scale(1.5);
opacity: 0;
}
}Survey-Specific Patterns
Survey Progress Indicator
tsx
function SurveyProgressIndicator() {
const { voiceState, surveyState } = useSibilance({
surveyKey: 'sibilance_your_key_here'
});
return (
<div className="survey-progress">
{voiceState.isConnected && (
<>
{voiceState.isUserSpeaking && (
<div className="status listening">
🎤 Listening to your response...
</div>
)}
{voiceState.isAIThinking && (
<div className="status thinking">
🤔 Processing your answer...
</div>
)}
{voiceState.isAISpeaking && (
<div className="status speaking">
🔊 {surveyState.currentStepId ? 'Next question...' : 'Speaking...'}
</div>
)}
{!voiceState.isUserSpeaking &&
!voiceState.isAIThinking &&
!voiceState.isAISpeaking && (
<div className="status idle">
Ready for your response
</div>
)}
</>
)}
</div>
);
}Conditional UI Based on State
tsx
function SurveyUI() {
const { voiceState, surveyState } = useSibilance({
surveyKey: 'sibilance_your_key_here'
});
// Disable form inputs while AI is speaking
const isInputDisabled = voiceState.isAISpeaking || voiceState.isAIThinking;
return (
<div>
<form>
<input
type="text"
disabled={isInputDisabled}
placeholder="Type your answer (or speak)"
/>
<button disabled={isInputDisabled}>
Submit
</button>
</form>
{voiceState.isUserSpeaking && (
<div className="recording-indicator">
Recording your response...
</div>
)}
</div>
);
}Advanced Patterns
State-Based Analytics
typescript
const client = new SibilanceClient({
surveyKey: 'sibilance_your_key_here'
});
let speakingTime = 0;
let listeningTime = 0;
let startTime = Date.now();
client.onStateChange((state) => {
const now = Date.now();
const elapsed = now - startTime;
if (state.isUserSpeaking) {
speakingTime += elapsed;
} else if (state.isAISpeaking) {
listeningTime += elapsed;
}
startTime = now;
});
// Get analytics
function getAnalytics() {
return {
totalSpeakingTime: speakingTime,
totalListeningTime: listeningTime,
ratio: speakingTime / listeningTime
};
}Auto-Pause on User Input
tsx
function AutoPauseSurvey() {
const { voiceState, pause, resume } = useSibilance({
surveyKey: 'sibilance_your_key_here'
});
const [isTyping, setIsTyping] = useState(false);
useEffect(() => {
if (isTyping && voiceState.isAISpeaking) {
pause();
} else if (!isTyping && !voiceState.isAISpeaking) {
resume();
}
}, [isTyping, voiceState.isAISpeaking]);
return (
<input
onFocus={() => setIsTyping(true)}
onBlur={() => setIsTyping(false)}
placeholder="Type your answer"
/>
);
}Related
- Survey Integration - Complete survey integration patterns
- React Integration - React-specific patterns
- Sibilance Client - Core client API