Syncfusion AI Assistant

How can I help you?

Speech-to-Text in TypeScript Chat UI

22 Mar 202612 minutes to read

The Syncfusion TypeScript Chat UI control integrates Speech-to-Text functionality through the browser’s Web Speech API. This enables the conversion of spoken words into text using the device’s microphone, allowing users to interact with the Chat UI through voice input.

Configure Speech-to-Text

To enable Speech-to-Text functionality in the TypeScript Chat UI control, update the index.ts file to incorporate the Web Speech API.

The SpeechToText control listens to audio input from the device’s microphone, transcribes spoken words into text, and updates the Chat UI’s editable footer using the footerTemplate property to display the transcribed text. Once the transcription appears in the footer, users can send it as a message to others.

Configuration Options

  • lang: Specifies the language for speech recognition. For example:

    • en-US for American English
    • fr-FR for French
  • allowInterimResults: Set to true to receive real-time (interim) recognition results, or false to receive only final results.

import { ChatUI, UserModel, MessageModel } from '@syncfusion/ej2-interactive-chat';
import { SpeechToText, TranscriptChangedEventArgs } from '@syncfusion/ej2-inputs';

let currentUserModel: UserModel = {
    id: "user1",
    user: "Albert"
};

let michaleUserModel: UserModel = {
    id: "user2",
    user: "Michale Suyama"
};
let chatMessages: MessageModel[] = [
    {
        author: currentUserModel,
        text: "Hi Michale, are we on track for the deadline?"
    },
    {
        author: michaleUserModel,
        text: "Yes, the design phase is complete."
    },
    {
        author: currentUserModel,
        text: "I’ll review it and send feedback by today."
    }
];
// Initializes the Chat UI control
let chatUI: ChatUI = new ChatUI({
    messages: chatMessages,
    user: currentUserModel,
    footerTemplate: "#footerContent"
});

// Render initialized Chat UI.
chatUI.appendTo('#chatui');

// Initialize Speech-to-Text component
let speechToTextObj: SpeechToText = new SpeechToText({
    transcriptChanged: onTranscriptChange,
    onStop: onListeningStop,
    created: onCreated,
    cssClass: 'e-flat',
});
speechToTextObj.appendTo('#speechToText');

// Handles actions when speech listening stops
function onListeningStop() {
    toggleButtons();
}

function onTranscriptChange(args: TranscriptChangedEventArgs): void {
    (document.querySelector('#chatui-footer') as HTMLElement).innerText = args.transcript;
}

// Handles actions after component creation
function onCreated(): void {
    let chatuiFooter = document.querySelector('#chatui-footer') as HTMLElement;
    let sendButton = document.querySelector('#chatui-sendButton') as HTMLElement;

    sendButton.addEventListener('click', sendIconClicked);
    chatuiFooter.addEventListener('input', toggleButtons);

    chatuiFooter.addEventListener('keydown', function (e) {
        if (e.key === 'Enter' && !e.shiftKey) {
            sendIconClicked();
            e.preventDefault(); // Prevent the default behavior of the Enter key
        }
    });
    toggleButtons();
}

// Toggles the visibility of the send and speech-to-text buttons
function toggleButtons(): void {
    let chatuiFooter = document.querySelector('#chatui-footer') as HTMLElement;
    let sendButton = document.querySelector('#chatui-sendButton') as HTMLElement;
    let speechButton = document.querySelector('#speechToText') as HTMLElement;

    let hasText = chatuiFooter.innerText.trim() !== '';
    sendButton.classList.toggle('visible', hasText);
    speechButton.classList.toggle('visible', !hasText);

    if (!hasText && (chatuiFooter.innerHTML === '<br>' || !chatuiFooter.innerHTML.trim())) {
        chatuiFooter.innerHTML = '';
    }
}

// Handles send button click event
function sendIconClicked(): void {
    var editor = document.querySelector('#chatui-footer') as HTMLElement;
    const messageContent = editor?.innerText || '';
    if (messageContent.trim()) {
        chatUI.addMessage({
        author: currentUserModel,
        text: messageContent,
      });
      editor.innerText = '';
      toggleButtons(); // Update button visibility
    }
}
<!DOCTYPE html>
<html lang="en">

<head>
    <title>EJ2 Chat UI</title>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta name="description" content="TypeScript Chat UI Control" />
    <meta name="author" content="Syncfusion" />
    <link href="index.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/33.1.44/ej2-base/styles/material.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/33.1.44/ej2-interactive-chat/styles/material.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/33.1.44/ej2-inputs/styles/material.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/33.1.44/ej2-buttons/styles/material.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/33.1.44/ej2-navigations/styles/material.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/33.1.44/ej2-notifications/styles/material.css" rel="stylesheet" />
    <script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.19.38/system.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
    <script src="systemjs.config.js"></script>
    <style>
        .integration-speechtotext {
            height: 400px;
            width: 450px;
            margin: 0 auto;
        }

        .integration-speechtotext #chatui-sendButton {
            width: 40px;
            height: 40px;
            font-size: 15px;
            border: none;
            background: none;
            cursor: pointer;
        }

        .integration-speechtotext #speechToText.visible,
        .integration-speechtotext #chatui-sendButton.visible {
            display: inline-block;
        }

        .integration-speechtotext #speechToText,
        .integration-speechtotext #chatui-sendButton {
            display: none;
        }

        @media only screen and (max-width: 750px) {
            .integration-speechtotext {
                width: 100%;
            }
        }

        .integration-speechtotext .e-footer-wrapper {
            display: flex;
            border: 1px solid #c1c1c1;
            margin: 5px 5px 0 5px;
            border-radius: 10px;
            padding: 5px;
        }

        .integration-speechtotext .content-editor {
            width: 100%;
            overflow-y: auto;
            font-size: 14px;
            min-height: 20px;
            max-height: 150px;
            padding: 10px;
        }

        .integration-speechtotext .content-editor[contentEditable='true']:empty:before {
            content: attr(placeholder);
            color: #6b7280;
            font-style: italic;
        }

        .integration-speechtotext .option-container {
            align-self: flex-end;
        }
    </style>
</head>
<body>
    <div id='loader'>Loading....</div>
    <div class="integration-speechtotext">
      <div id="chatui"></div>
    </div>
    <script id="footerContent" type="text/x-jsrender">
      <div class="e-footer-wrapper">
          <div id="chatui-footer" class="content-editor" contenteditable="true" placeholder="Click to speak or start typing..."></div>
          <div class="option-container">
              <button id="speechToText"></button>
              <button id="chatui-sendButton" class="e-assist-send e-icons" role="button"></button>
          </div>
      </div>
    </script>
</body>
</html>

Error Handling

The SpeechToText control provides events to handle errors that may occur during speech recognition. For more information, refer to the Error Handling section in the documentation.

Browser Compatibility

The SpeechToText control relies on the Speech Recognition API, which has limited browser support. Refer to the Browser Compatibility section for detailed information.

See Also