Speech recognition in EJ2 Vue SpeechToText component

26 Mar 202524 minutes to read

Retrieving transcripts

You can use the transcript property to retrieve the transcribed text from the spoken text. This property allows to display the transcribed text once the speech recognition process is started.

<template>
  <div id='container'>
    <ejs-speechtotext id="speechtotext" ref="speechToTextInstance" v-model="transcript" @transcript-changed="onTranscriptChange"></ejs-speechtotext>
    <ejs-textarea v-model="transcript" rows="5" cols="50" resizeMode="None" placeholder="Transcribed text will be shown here..."></ejs-textarea>
  </div>
</template>

<script setup>
import { ref } from "vue";
import { SpeechToTextComponent as EjsSpeechtotext, TextAreaComponent as EjsTextarea } from "@syncfusion/ej2-vue-inputs";

const transcript = ref('Hi, hello! How are you?');
const speechToTextInstance = ref(null);

const onTranscriptChange = (args) => {
  transcript.value = speechToTextInstance.value.ej2Instances.transcript;
};

</script>

<style>
  @import '../node_modules/@syncfusion/ej2-base/styles/material.css';
  @import '../node_modules/@syncfusion/ej2-buttons/styles/material.css';
  @import '../node_modules/@syncfusion/ej2-popups/styles/material.css';
  @import '../node_modules/@syncfusion/ej2-inputs/styles/material.css';

  #container {
    margin: 50px auto;
    gap: 20px;
    display: flex;
    flex-direction: column;
    align-items: center;
  }
</style>
<template>
  <div id='container'>
    <ejs-speechtotext id="speechtotext" ref="speechToTextInstance" @transcript-changed="onTranscriptChange" v-model="transcript"></ejs-speechtotext>
    <ejs-textarea v-model="transcript" rows="5" cols="50" resizeMode="None" placeholder="Transcribed text will be shown here..."></ejs-textarea>
  </div>
</template>

<script>
import { SpeechToTextComponent, TextAreaComponent  } from "@syncfusion/ej2-vue-inputs";
import { enableRipple } from '@syncfusion/ej2-base';
enableRipple(true);

export default {
  components: {
    "ejs-speechtotext": SpeechToTextComponent,
    "ejs-textarea": TextAreaComponent
  },
  data() {
    return {
      transcript: 'Hi, hello! How are you?'
    };
  },
  methods: {
    onTranscriptChange: function(args) {
      this.transcript = this.$refs.speechToTextInstance.ej2Instances.transcript;
    }
  }
}
</script>

<style>
  @import '../node_modules/@syncfusion/ej2-base/styles/material.css';
  @import '../node_modules/@syncfusion/ej2-buttons/styles/material.css';
  @import '../node_modules/@syncfusion/ej2-popups/styles/material.css';
  @import '../node_modules/@syncfusion/ej2-inputs/styles/material.css';

  #container {
    margin: 50px auto;
    gap: 20px;
    display: flex;
    flex-direction: column;
    align-items: center;
  }
</style>

Setting language

You can use the lang property to specify the language for speech recognition. Setting this property ensures that the recognition engine interprets the spoken words correctly based on the specified locale such as en-US for American English, fr-FR for French, and more.

<template>
  <div id='container'>
    <ejs-speechtotext id="speechtotext" @transcript-changed="onTranscriptChange" lang="fr-FR"></ejs-speechtotext>
    <ejs-textarea v-model="textareaValue" rows="5" cols="50" resizeMode="None" placeholder="Transcribed text will be shown here..."></ejs-textarea>
  </div>
</template>

<script setup>
import { ref } from "vue";
import { SpeechToTextComponent as EjsSpeechtotext, TextAreaComponent as EjsTextarea } from "@syncfusion/ej2-vue-inputs";

const textareaValue = ref('');

const onTranscriptChange = (args) => {
  textareaValue.value = args.transcript;
};

</script>

<style>
  @import '../node_modules/@syncfusion/ej2-base/styles/material.css';
  @import '../node_modules/@syncfusion/ej2-buttons/styles/material.css';
  @import '../node_modules/@syncfusion/ej2-popups/styles/material.css';
  @import '../node_modules/@syncfusion/ej2-inputs/styles/material.css';

  #container {
    margin: 50px auto;
    gap: 20px;
    display: flex;
    flex-direction: column;
    align-items: center;
  }
</style>
<template>
    <div id='container'>
      <ejs-speechtotext id="speechtotext" @transcriptChanged="onTranscriptChange" lang="fr-FR"></ejs-speechtotext>
      <ejs-textarea ref="textareaObj" rows="5" cols="50" value="" resize-mode="None" placeholder="Transcribed text will be shown here..."></ejs-textarea>
  </div>
</template>

<script>
import { SpeechToTextComponent, TextAreaComponent  } from "@syncfusion/ej2-vue-inputs";
import { enableRipple } from '@syncfusion/ej2-base';
enableRipple(true);

export default {
  components: {
    "ejs-speechtotext": SpeechToTextComponent,
    "ejs-textarea": TextAreaComponent
  },
  data() {
    return {
    };
  },
  methods: {
    onTranscriptChange: function(args) {
      this.$refs.textareaObj.ej2Instances.value = args.transcript;
    }
  }
}
</script>

<style>
  @import '../node_modules/@syncfusion/ej2-base/styles/material.css';
  @import '../node_modules/@syncfusion/ej2-buttons/styles/material.css';
  @import '../node_modules/@syncfusion/ej2-popups/styles/material.css';
  @import '../node_modules/@syncfusion/ej2-inputs/styles/material.css';

  #container {
    margin: 50px auto;
    gap: 20px;
    display: flex;
    flex-direction: column;
    align-items: center;
  }
</style>

Allowing interim results

You can use the allowInterimResults property to enable or disable interim results. When set to true, the recognized speech will be displayed in real time as words are spoken. When set to false, only final results will be displayed after recognition is complete. By default, the value is true.

<template>
  <div id='container'>
    <ejs-speechtotext id="speechtotext" @transcript-changed="onTranscriptChange" :allow-interim-results="false"></ejs-speechtotext>
    <ejs-textarea v-model="textareaValue" rows="5" cols="50" resizeMode="None" placeholder="Transcript will be displayed here once speech recognition is complete."></ejs-textarea>
  </div>
</template>

<script setup>
import { ref } from "vue";
import { SpeechToTextComponent as EjsSpeechtotext, TextAreaComponent as EjsTextarea } from "@syncfusion/ej2-vue-inputs";

const textareaValue = ref('');

const onTranscriptChange = (args) => {
  textareaValue.value = args.transcript;
};

</script>

<style>
  @import '../node_modules/@syncfusion/ej2-base/styles/material.css';
  @import '../node_modules/@syncfusion/ej2-buttons/styles/material.css';
  @import '../node_modules/@syncfusion/ej2-popups/styles/material.css';
  @import '../node_modules/@syncfusion/ej2-inputs/styles/material.css';

  #container {
    margin: 50px auto;
    gap: 20px;
    display: flex;
    flex-direction: column;
    align-items: center;
  }
</style>
<template>
    <div id='container'>
      <ejs-speechtotext id="speechtotext" @transcriptChanged="onTranscriptChange" :allow-interim-results="false"></ejs-speechtotext>
      <ejs-textarea ref="textareaObj" rows="5" cols="50" value="" resize-mode="None" placeholder="Transcript will be displayed here once speech recognition is complete."></ejs-textarea>
  </div>
</template>

<script>
import { SpeechToTextComponent, TextAreaComponent  } from "@syncfusion/ej2-vue-inputs";
import { enableRipple } from '@syncfusion/ej2-base';
enableRipple(true);

export default {
  components: {
    "ejs-speechtotext": SpeechToTextComponent,
    "ejs-textarea": TextAreaComponent
  },
  data() {
    return {

    };
  },
  methods: {
    onTranscriptChange: function(args) {
      this.$refs.textareaObj.ej2Instances.value = args.transcript;
    }
  }
}
</script>

<style>
  @import '../node_modules/@syncfusion/ej2-base/styles/material.css';
  @import '../node_modules/@syncfusion/ej2-buttons/styles/material.css';
  @import '../node_modules/@syncfusion/ej2-popups/styles/material.css';
  @import '../node_modules/@syncfusion/ej2-inputs/styles/material.css';

  #container {
    margin: 50px auto;
    gap: 20px;
    display: flex;
    flex-direction: column;
    align-items: center;
  }
</style>

Managing listening state

You can use the listeningState property to manage the listening state of the component. The possible values are Inactive, Listening and Stopped. By default, the value is Inactive.

Inactive

The component is in idle state with no active speech recognition.

Listening

It is actively listening which captures and transcribes speech with a stop icon and blinking animation.

Stopped

Denotes the speech recognition has ended, and no further speech is being processed.

Below sample demonstrates the usage of listeningState property.

<template>
  <div id="container">
    <div id="status-box-container" class="status-box inactive">
      <span>Status: <strong id="status-text">Inactive</strong></span>
    </div>
    <ejs-speechtotext id="speechtotext" listening-state="Inactive" @start="(args) => updateListeningState(args.listeningState)" @stop="(args) => updateListeningState(args.listeningState)"></ejs-speechtotext>
    <div class="waveform-container">
      <div id="waveform-item" class="waveform" style="display: none;">
        <span></span><span></span><span></span><span></span><span></span>
      </div>
      <p id="instruction-text">Click the button to start listening.</p>
    </div>
  </div>
</template>

<script setup>
import { SpeechToTextComponent as EjsSpeechtotext } from "@syncfusion/ej2-vue-inputs";

const updateListeningState = (state) => {
  document.getElementById("status-text").innerText = state;

  var statusBox = document.getElementById("status-box-container");
  var waveform = document.getElementById("waveform-item");
  var instructionText = document.getElementById("instruction-text");

  if (state === "Listening") {
    statusBox.className = "status-box listening";
    waveform.style.display = "flex";
    instructionText.innerText = "Listening... Speak now!";
  } else if (state === "Stopped") {
    statusBox.className = "status-box stopped";
    waveform.style.display = "none";
    instructionText.innerText = "Recognition Stopped.";
  } else {
    statusBox.className = "status-box inactive";
    waveform.style.display = "none";
    instructionText.innerText = "Click the button to start listening.";
  }
};
</script>

<style>
  @import '../node_modules/@syncfusion/ej2-base/styles/material.css';
  @import '../node_modules/@syncfusion/ej2-buttons/styles/material.css';
  @import '../node_modules/@syncfusion/ej2-popups/styles/material.css';
  @import '../node_modules/@syncfusion/ej2-inputs/styles/material.css';

  .waveform-container {
    margin-top: 20px;
    font-weight: bold;
  }

  .waveform {
    display: flex;
    justify-content: center;
    align-items: center;
    height: 40px;
    gap: 5px;
  }

  .waveform span {
    display: block;
    width: 6px;
    height: 20px;
    background: #28a745;
    animation: wave-animation 1.2s infinite ease-in-out;
  }

  .waveform span:nth-child(1) {
    animation-delay: 0s;
  }

  .waveform span:nth-child(2) {
    animation-delay: 0.2s;
  }

  .waveform span:nth-child(3) {
    animation-delay: 0.4s;
  }

  .waveform span:nth-child(4) {
    animation-delay: 0.6s;
  }

  .waveform span:nth-child(5) {
    animation-delay: 0.8s;
  }

  @keyframes wave-animation {
    0%, 100% {
        height: 10px;
    }

    50% {
        height: 30px;
    }
  }

  #container {
    width: 400px;
    text-align: center;
    margin: 50px auto;
    padding: 20px;
    border-radius: 10px;
    box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.1);
    background: #fff;
  }

  .status-box {
    padding: 10px;
    border-radius: 5px;
    margin-bottom: 40px;
    font-weight: bold;
  }

  .status-box.listening {
    background-color: #d1e7dd;
    color: #0f5132;
  }

  .status-box.stopped {
    background-color: #f8d7da;
    color: #842029;
  }

  .status-box.inactive {
    background-color: #e2e3e5;
    color: #6c757d;
  }

  .visual-indicator {
    margin-top: 20px;
  }

</style>
<template>
  <div id="container">
    <div id="status-box-container" class="status-box inactive">
      <span>Status: <strong id="status-text">Inactive</strong></span>
    </div>
    <ejs-speechtotext id="speechtotext" listening-state="Inactive" @start="(args) => updateListeningState(args.listeningState)" @stop="(args) => updateListeningState(args.listeningState)"></ejs-speechtotext>
    <div class="waveform-container">
      <div id="waveform-item" class="waveform" style="display: none;">
        <span></span><span></span><span></span><span></span><span></span>
      </div>
      <p id="instruction-text">Click the button to start listening.</p>
    </div>
  </div>
</template>

<script>
import { SpeechToTextComponent  } from "@syncfusion/ej2-vue-inputs";
import { enableRipple } from '@syncfusion/ej2-base';
enableRipple(true);

export default {
  components: {
    "ejs-speechtotext": SpeechToTextComponent
  },
  data() {
    return {
    };
  },
  methods: {
    updateListeningState: function (state) {
      document.getElementById("status-text").innerText = state;

      var statusBox = document.getElementById("status-box-container");
      var waveform = document.getElementById("waveform-item");
      var instructionText = document.getElementById("instruction-text");

      if (state === "Listening") {
        statusBox.className = "status-box listening";
        waveform.style.display = "flex";
        instructionText.innerText = "Listening... Speak now!";
      } else if (state === "Stopped") {
        statusBox.className = "status-box stopped";
        waveform.style.display = "none";
        instructionText.innerText = "Recognition Stopped.";
      } else {
        statusBox.className = "status-box inactive";
        waveform.style.display = "none";
        instructionText.innerText = "Click the button to start listening.";
      }
    }
  }
}
</script>

<style>
  @import '../node_modules/@syncfusion/ej2-base/styles/material.css';
  @import '../node_modules/@syncfusion/ej2-buttons/styles/material.css';
  @import '../node_modules/@syncfusion/ej2-popups/styles/material.css';
  @import '../node_modules/@syncfusion/ej2-inputs/styles/material.css';


  .waveform-container {
    margin-top: 20px;
    font-weight: bold;
  }

  .waveform {
    display: flex;
    justify-content: center;
    align-items: center;
    height: 40px;
    gap: 5px;
  }

  .waveform span {
    display: block;
    width: 6px;
    height: 20px;
    background: #28a745;
    animation: wave-animation 1.2s infinite ease-in-out;
  }

  .waveform span:nth-child(1) {
    animation-delay: 0s;
  }

  .waveform span:nth-child(2) {
    animation-delay: 0.2s;
  }

  .waveform span:nth-child(3) {
    animation-delay: 0.4s;
  }

  .waveform span:nth-child(4) {
    animation-delay: 0.6s;
  }

  .waveform span:nth-child(5) {
    animation-delay: 0.8s;
  }

  @keyframes wave-animation {
    0%, 100% {
        height: 10px;
    }

    50% {
        height: 30px;
    }
  }

  #container {
    width: 400px;
    text-align: center;
    margin: 50px auto;
    padding: 20px;
    border-radius: 10px;
    box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.1);
    background: #fff;
  }

  .status-box {
    padding: 10px;
    border-radius: 5px;
    margin-bottom: 40px;
    font-weight: bold;
  }

  .status-box.listening {
    background-color: #d1e7dd;
    color: #0f5132;
  }

  .status-box.stopped {
    background-color: #f8d7da;
    color: #842029;
  }

  .status-box.inactive {
    background-color: #e2e3e5;
    color: #6c757d;
  }

  .visual-indicator {
    margin-top: 20px;
  }

</style>

Show or hide tooltip

You can use the showTooltip property to specify the tooltip text to be displayed on hovering the SpeechToText button. By default, the value is true.

<template>
  <div id='container'>
    <ejs-speechtotext id="speechtotext" @transcript-changed="onTranscriptChange" :show-tooltip="false"></ejs-speechtotext>
    <ejs-textarea v-model="textareaValue" rows="5" cols="50" resizeMode="None" placeholder="Transcribed text will be shown here..."></ejs-textarea>
  </div>
</template>

<script setup>
import { ref } from "vue";
import { SpeechToTextComponent as EjsSpeechtotext, TextAreaComponent as EjsTextarea } from "@syncfusion/ej2-vue-inputs";

const textareaValue = ref('');

const onTranscriptChange = (args) => {
  textareaValue.value = args.transcript;
};

</script>

<style>
  @import '../node_modules/@syncfusion/ej2-base/styles/material.css';
  @import '../node_modules/@syncfusion/ej2-buttons/styles/material.css';
  @import '../node_modules/@syncfusion/ej2-popups/styles/material.css';
  @import '../node_modules/@syncfusion/ej2-inputs/styles/material.css';

  #container {
    margin: 50px auto;
    gap: 20px;
    display: flex;
    flex-direction: column;
    align-items: center;
  }
</style>
<template>
    <div id='container'>
      <ejs-speechtotext id="speechtotext" @transcriptChanged="onTranscriptChange" :show-tooltip="false"></ejs-speechtotext>
      <ejs-textarea ref="textareaObj" rows="5" cols="50" value="" resize-mode="None" placeholder="Transcribed text will be shown here..."></ejs-textarea>
  </div>
</template>

<script>
import { SpeechToTextComponent, TextAreaComponent  } from "@syncfusion/ej2-vue-inputs";
import { enableRipple } from '@syncfusion/ej2-base';
enableRipple(true);

export default {
  components: {
    "ejs-speechtotext": SpeechToTextComponent,
    "ejs-textarea": TextAreaComponent
  },
  data() {
    return {

    };
  },
  methods: {
    onTranscriptChange: function(args) {
      this.$refs.textareaObj.ej2Instances.value = args.transcript;
    }
  }
}
</script>

<style>
  @import '../node_modules/@syncfusion/ej2-base/styles/material.css';
  @import '../node_modules/@syncfusion/ej2-buttons/styles/material.css';
  @import '../node_modules/@syncfusion/ej2-popups/styles/material.css';
  @import '../node_modules/@syncfusion/ej2-inputs/styles/material.css';

  #container {
    margin: 50px auto;
    gap: 20px;
    display: flex;
    flex-direction: column;
    align-items: center;
  }
</style>

Setting disabled

You can use the disabled property to disable the SpeechToText, preventing user interaction when set to true. By default, the value is false.

<template>
  <div id='container'>
    <ejs-speechtotext id="speechtotext" @transcript-changed="onTranscriptChange" :disabled="true"></ejs-speechtotext>
    <ejs-textarea v-model="textareaValue" rows="5" cols="50" resizeMode="None" placeholder="Transcribed text will be shown here..."></ejs-textarea>
  </div>
</template>

<script setup>
import { ref } from "vue";
import { SpeechToTextComponent as EjsSpeechtotext, TextAreaComponent as EjsTextarea } from "@syncfusion/ej2-vue-inputs";

const textareaValue = ref('');

const onTranscriptChange = (args) => {
  textareaValue.value = args.transcript;
};

</script>

<style>
  @import '../node_modules/@syncfusion/ej2-base/styles/material.css';
  @import '../node_modules/@syncfusion/ej2-buttons/styles/material.css';
  @import '../node_modules/@syncfusion/ej2-popups/styles/material.css';
  @import '../node_modules/@syncfusion/ej2-inputs/styles/material.css';

  #container {
    margin: 50px auto;
    gap: 20px;
    display: flex;
    flex-direction: column;
    align-items: center;
  }
</style>
<template>
    <div id='container'>
      <ejs-speechtotext id="speechtotext" @transcriptChanged="onTranscriptChange" :disabled="true"></ejs-speechtotext>
      <ejs-textarea ref="textareaObj" rows="5" cols="50" value="" resize-mode="None" placeholder="Transcribed text will be shown here..."></ejs-textarea>
  </div>
</template>

<script>
import { SpeechToTextComponent, TextAreaComponent  } from "@syncfusion/ej2-vue-inputs";
import { enableRipple } from '@syncfusion/ej2-base';
enableRipple(true);

export default {
  components: {
    "ejs-speechtotext": SpeechToTextComponent,
    "ejs-textarea": TextAreaComponent
  },
  data() {
    return {

    };
  },
  methods: {
    onTranscriptChange: function(args) {
      this.$refs.textareaObj.ej2Instances.value = args.transcript;
    }
  }
}
</script>

<style>
  @import '../node_modules/@syncfusion/ej2-base/styles/material.css';
  @import '../node_modules/@syncfusion/ej2-buttons/styles/material.css';
  @import '../node_modules/@syncfusion/ej2-popups/styles/material.css';
  @import '../node_modules/@syncfusion/ej2-inputs/styles/material.css';

  #container {
    margin: 50px auto;
    gap: 20px;
    display: flex;
    flex-direction: column;
    align-items: center;
  }
</style>

Setting html attributes

You can use the htmlAttributes property to assign custom attributes to the SpeechToText component for the button element.

Error handling

The SpeechToText component handles various errors that may occur during speech recognition. The following table lists the possible errors and their causes:

Error Cause
no-speech The microphone did not detect any speech input.
aborted The speech recognition process was intentionally terminated.
audio-capture The system was unable to detect a microphone device.
not-allowed Access to the microphone was denied by the user or browser settings.
service-not-allowed The current context does not permit the use of the speech recognition service.
network A network issue is preventing the speech recognition service from functioning.
unsupported-browser The browser being used does not support the SpeechRecognition API.
default An unidentified error occurred during the speech recognition process.

Browser support

The SpeechToText component relies on the Speech Recognition API for processing the speech input. Ensure that the browser supports this API before implementation.

Browser Supported versions
Chrome 25+
Edge 79+
Firefox Not Supported
Safari 12+
Opera 30+