Web Speech API Specification

repository·main·Indexed 41 days ago

https://github.com/webaudio/web-speech-api

Specification for the Web Speech API, currently in unofficial incubation by the Web Audio Community Group. Includes documentation on contextual biasing via SpeechRecognitionPhrase, on-device speech recognition using SpeechRecognitionOptions and SpeechRecognitionQuality, and acoustic boundary timestamps (speechStartTime and speechEndTime) for synchronized subtitles and click-to-seek navigation.

Tokens
6.3K
Snippets
15
Records
19
Agent score
52%

What's inside Web Speech API

  1. Implementation details for unspoken punctuation

    main

    When using unspokenPunctuation: true, developers should be aware of the following implementation behaviors:

    • Automatic Capitalization: Automatic punctuation is often coupled with automatic capitalization. The engine may automatically capitalize the first word following an inferred sentence-ending punctuation mark. Developers should not assume the text will remain strictly lowercase.
    • Internationalization: Punctuation and spacing rules are implementation-dependent and should follow localized rules based on the SpeechRecognition.lang attribute (e.g., using ¿ in Spanish).
  2. Improve recognition accuracy with Contextual Biasing

    main

    Contextual biasing allows you to provide "hints" to the speech recognition engine to improve accuracy for domain-specific terminology, proper nouns, or uncommon words. By providing a list of likely phrases and associated boost values, you increase the probability that the engine correctly identifies these specific terms instead of more common, acoustically similar words.

    This is particularly useful for:

    • Specialized terminology: Medical, legal, or scientific terms.
    • Proper nouns: Character names in games or brand names in e-commerce.
    • Domain-specific commands: Specific vocabulary used within a particular application context.
  3. How on-device speech recognition works

    main

    On-device speech recognition is an enhancement to the Web Speech API that allows audio processing to happen locally on the user's device instead of relying on cloud-based services.

    Key Benefits:

    • Privacy: Raw audio and transcriptions stay on the device, helping with data residency and compliance (e.g., GDPR).
    • Performance: Local processing reduces network latency and can support features like contextual biasing.
    • Offline Functionality: Enables speech recognition in environments without internet connectivity.

    Workflow:

    1. Define SpeechRecognitionOptions with processLocally: true and the desired langs.
    2. Check availability using SpeechRecognition.available(options).
    3. If status is "downloadable", call SpeechRecognition.install(options) to fetch language models.
    4. Initialize and start SpeechRecognition with the configured options.
  4. Check and install on-device speech models with quality constraints

    main

    To implement progressive enhancement for speech recognition, use SpeechRecognition.available(options) to check for a model that meets your required quality level, and SpeechRecognition.install(options) to attempt to download one if it is missing. If install() rejects, it typically means the device hardware (NPU/RAM) cannot support the requested quality level, and you should fall back to cloud-based recognition.

    const meetConfig = {
      langs: ['en-US'],
      processLocally: true,
      quality: 'conversation' 
    };
    
    async function setupSpeech() {
      // 1. Check if a high-quality model is already available
      const availability = await SpeechRecognition.available(meetConfig);
      
      if (availability == 'available') {
        startOnDeviceSpeech();
        return;
      }
    
      // 2. If not, try to install one
      try {
        await SpeechRecognition.install(meetConfig);
        startOnDeviceSpeech();
      } catch (e) {
        // 3. Fallback: Device hardware is too weak or user denied download.
        // Fallback to Cloud to ensure transcript usability.
        console.log("High-quality on-device model unavailable. Using Cloud.");
        startCloudSpeech();
      }
    }
  5. Handle contextual biasing errors

    main

    When implementing contextual biasing, you should check for errors in the onerror event handler. If the browser or speech service does not support biasing, the event.error will return the string 'phrases-not-supported'.

    recognition.onerror = (event) => {
      if (event.error === 'phrases-not-supported') {
        console.warn('Contextual biasing is not supported by this browser/service.');
      }
    };
  6. Example: Using unspoken punctuation for natural transcription

    main

    This example demonstrates how to enable unspokenPunctuation alongside other standard settings like continuous and interimResults to capture natural, punctuated speech.

    When unspokenPunctuation is enabled, the transcript in a final result will include inferred punctuation (e.g., "Hello there, how are you today?") instead of raw text ("hello there how are you today").

    const recognition = new SpeechRecognition();
    
    // Enable unspoken punctuation
    recognition.unspokenPunctuation = true;
    
    // Configure other settings
    recognition.continuous = true;
    recognition.interimResults = true;
    recognition.lang = 'en-US';
    
    recognition.onresult = (event) => {
      for (let i = event.resultIndex; i < event.results.length; ++i) {
        if (event.results[i].isFinal) {
          console.log('Final Result with Punctuation: ', event.results[i][0].transcript);
          // Example output: "Hello there, how are you today?"
        } else {
          console.log('Interim Result: ', event.results[i][0].transcript);
        }
      }
    };
    
    recognition.start();
  7. Full example of implementing Contextual Biasing

    main

    This example demonstrates how to create SpeechRecognitionPhrase objects, assign them to a SpeechRecognition instance, and handle potential errors.

    // A list of phrases relevant to our application's context.
    const phraseData = [
      { phrase: 'Zoltan', boost: 3.0 },
      { phrase: 'Grog', boost: 2.0 },
    ];
    
    // Create SpeechRecognitionPhrase objects.
    const phraseObjects = phraseData.map(p => new SpeechRecognitionPhrase(p.phrase, p.boost));
    
    const recognition = new SpeechRecognition();
    
    // Assign the phrase objects to the recognition instance.
    recognition.phrases = phraseObjects;
    
    // We can also dynamically add/remove phrases.
    recognition.phrases.push(new SpeechRecognitionPhrase('Xylia', 2.5));
    
    // Some user agents (e.g. Chrome) might only support on-device contextual biasing.
    recognition.processLocally = true;
    
    recognition.onresult = (event) => {
      const transcript = event.results[0][0].transcript;
      console.log(`Result: ${transcript}`);
    };
    
    recognition.onerror = (event) => {
      if (event.error === 'phrases-not-supported') {
        console.warn('Contextual biasing is not supported by this browser/service.');
      }
    };
    
    // Start recognition when the user clicks a button.
    document.getElementById('speak-button').onclick = () => {
      recognition.start();
    };
  8. Create synchronized subtitles and click-to-seek navigation

    main

    You can use speechStartTime and speechEndTime to generate synchronized WebVTT captions and interactive transcript elements that allow users to jump to specific parts of a media playback.

    1. Captions: Use the timestamps to instantiate a VTTCue and add it to a media track.
    2. Click-to-Seek: Create DOM elements for each transcript segment. When clicked, set the mediaElement.currentTime to the result.speechStartTime to jump the playback to that exact moment.
    const mediaElement = document.querySelector('video');
    const track = mediaElement.addTextTrack('captions', 'English', 'en');
    track.mode = 'showing';
    
    const recognition = new SpeechRecognition();
    recognition.continuous = true;
    recognition.interimResults = false;
    
    // Transcribe audio track from HTMLMediaElement
    const audioStream = mediaElement.captureStream();
    recognition.start(audioStream.getAudioTracks()[0]);
    
    recognition.onresult = (event) => {
      for (let i = event.resultIndex; i < event.results.length; ++i) {
        const result = event.results[i];
        if (!result.isFinal) continue;
    
        const transcriptText = result[0].transcript;
    
        // 1. Create a synchronized WebVTT caption cue
        const cue = new VTTCue(result.speechStartTime, result.speechEndTime, transcriptText);
        track.addCue(cue);
    
        // 2. Build interactive transcript element ("Click-to-Seek")
        const transcriptItem = document.createElement('p');
        transcriptItem.textContent = `[${result.speechStartTime.toFixed(1)}s] ${transcriptText}`;
        transcriptItem.onclick = () => {
          mediaElement.currentTime = result.speechStartTime;
          mediaElement.play();
        };
        document.getElementById('transcript-container').appendChild(transcriptItem);
      }
    };
  9. Implement live captions and recording timeline association

    main

    For live video conferencing, you can use the timestamps to provide two distinct experiences:

    1. Live Display: Render interim results immediately on screen to minimize perceived lag. Use result.isFinal to manage the visual state (e.g., styling interim vs. final text).
    2. Recording Archive: When a result is finalized (isFinal), archive the text along with its speechStartTime and speechEndTime. This allows you to reconstruct frame-accurate subtitle tracks for recorded media during post-meeting replay.
    const recognition = new SpeechRecognition();
    recognition.continuous = true;
    recognition.interimResults = true;
    
    let hideTimeout = null;
    const liveSubtitleOverlay = document.getElementById('live-captions');
    const meetingTranscript = [];
    
    recognition.onresult = (event) => {
      const result = event.results[event.resultIndex];
      const transcriptText = result[0].transcript;
    
      // Render live captions immediately on screen
      liveSubtitleOverlay.textContent = transcriptText;
      liveSubtitleOverlay.classList.toggle('interim', !result.isFinal);
    
      // Keep final captions on screen for a 3-second reading persistence window
      clearTimeout(hideTimeout);
      if (result.isFinal) {
        hideTimeout = setTimeout(() => {
          liveSubtitleOverlay.textContent = '';
        }, 3000);
    
        // Archive the finalized sentence with exact acoustic timestamps
        meetingTranscript.push({
          text: transcriptText,
          startTime: result.speechStartTime,
          endTime: result.speechEndTime
        });
      }
    };
    
    // Post-Meeting Replay: Synchronize caption cues with the recorded meeting video
    function initializeRecordingCaptions(recordedVideoElement) {
      const captionTrack = recordedVideoElement.addTextTrack('captions', 'Meeting Captions');
      captionTrack.mode = 'showing';
    
      for (const entry of meetingTranscript) {
        captionTrack.addCue(new VTTCue(entry.startTime, entry.endTime, entry.text));
      }
    }
  10. Configure SpeechRecognition.phrases

    main

    The SpeechRecognition.phrases attribute is an ObservableArray<SpeechRecognitionPhrase> that accepts contextual hints for the recognition session. You can interact with it like a standard JavaScript Array (e.g., using .push()) or assign a new array of SpeechRecognitionPhrase objects to it.

    Note on Browser Support: Some user agents (such as Chrome) may only support on-device contextual biasing. You can attempt to enable this by setting recognition.processLocally = true.

    const recognition = new SpeechRecognition();
    
    // Assigning an array of phrase objects
    recognition.phrases = [
      new SpeechRecognitionPhrase('Zoltan', 3.0),
      new SpeechRecognitionPhrase('Grog', 2.0)
    ];
    
    // Dynamically adding a phrase
    recognition.phrases.push(new SpeechRecognitionPhrase('Xylia', 2.5));
  11. Extend SpeechRecognitionResult with speechStartTime and speechEndTime

    main

    The SpeechRecognitionResult interface can be extended with two new read-only attributes to provide the exact acoustic boundaries of a recognized speech segment. These timestamps are measured in seconds relative to the start of the audio stream ($t = 0.0\text{s}$), which is marked by the audiostart event.

    • speechStartTime: The start timestamp of the speech segment in seconds.
    • speechEndTime: The end timestamp of the speech segment in seconds.

    Using seconds as a double ensures compatibility with the Web Audio API and HTML Media Elements, allowing for seamless synchronization without manual unit conversions.

    partial interface SpeechRecognitionResult {
        // Start timestamp of the speech segment in seconds relative to the start of the audio stream (0.0s).
        readonly attribute double speechStartTime;
    
        // End timestamp of the speech segment in seconds relative to the start of the audio stream.
        readonly attribute double speechEndTime;
    };