From 25aa0116cdb16dc4a401bfb7ad8cff4b82a337bb Mon Sep 17 00:00:00 2001 From: Jatin K Malik Date: Thu, 12 Feb 2026 00:57:34 -0800 Subject: [PATCH] fix(web): prevent speech result duplication on mobile browsers (#186) --- web/src/components/live-demo.tsx | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/web/src/components/live-demo.tsx b/web/src/components/live-demo.tsx index 97f5d16..9a6dcc7 100644 --- a/web/src/components/live-demo.tsx +++ b/web/src/components/live-demo.tsx @@ -126,6 +126,8 @@ export function LiveDemo() { const [detectedLanguage, setDetectedLanguage] = useState("en-US"); const recognitionRef = useRef(null); const isListeningRef = useRef(false); + // Track the index up to which results have been processed to prevent duplicates on mobile + const processedResultIndexRef = useRef(0); // Keep ref in sync with state for use in event handlers useEffect(() => { @@ -163,20 +165,30 @@ export function LiveDemo() { }; recognition.onresult = (event: SpeechRecognitionEvent) => { + // Get the base index - on some browsers (especially mobile), resultIndex + // may be 0 but the array contains all previous results. We need to + // track which results we've already processed to avoid duplicates. + const startIndex = Math.max(event.resultIndex, processedResultIndexRef.current); + let interim = ""; let final = ""; - for (let i = event.resultIndex; i < event.results.length; i++) { + // Only process new results (from startIndex to end) + for (let i = startIndex; i < event.results.length; i++) { const transcript = event.results[i][0].transcript; if (event.results[i].isFinal) { - final += transcript; + final += transcript + " "; } else { interim += transcript; } } + // Update processed index to the end of current results + // Any results before this have been handled + processedResultIndexRef.current = event.results.length; + if (final) { - setTranscript((prev) => prev + final + " "); + setTranscript((prev) => prev + final); setInterimTranscript(""); } else { setInterimTranscript(interim); @@ -206,6 +218,7 @@ export function LiveDemo() { if (recognitionRef.current) { setTranscript(""); setInterimTranscript(""); + processedResultIndexRef.current = 0; // Reset processed result index try { recognitionRef.current.start(); } catch (e) {