Reach

Adding Voice Input

⏱ 12 min

A visitor who’d rather speak a question than type it isn’t asking for a WordPress feature at all, they’re asking for something the browser itself already does. There is no WordPress-specific voice input API, and no reason to invent one: the Web Speech API, a standard, browser-native capability, converts speech to text entirely on the client side, before any of it ever reaches your REST endpoint. By the time PHP sees anything, it’s the exact same plain-text message field Course 11 always sent.

What you'll learn in this lesson
Why this is a browser feature, not a WordPress one
The Web Speech API, standard and unrelated to the PHP AI Client SDK.
Wiring SpeechRecognition into the widget
A microphone button that fills the existing text input, nothing more.
Handling browsers that don't support it
Real, current support gaps, and a graceful fallback to typing.
Why the backend needs zero changes
Voice input never reaches PHP as anything other than text.
Prerequisites

Course 11’s chat widget UI and sendMessage() function. No PHP AI Client SDK prerequisite, this lesson is entirely client-side JavaScript.

Step 1: be clear about what this actually is

Not a WordPress feature, and not part of the AI Client SDK

SpeechRecognition is a standard Web API, implemented by the browser itself, with no relationship to wordpress/php-ai-client or any WordPress core capability. It converts audio to text using whatever speech recognition service the browser vendor provides, entirely outside your server. Your PHP code never sees audio, never sees a browser API call, and never needs to know voice input exists, it only ever receives the resulting text through the same message field Course 11 already built.

Step 2: add a microphone button that fills the text input

<!-- File: templates/chat-widget.php -->
<div class="site-chat-input-row">
	<textarea id="site-chat-input" placeholder="Type your question..."></textarea>
	<button type="button" id="site-chat-mic" aria-label="Speak your question">🎤</button>
	<button type="button" id="site-chat-send">Send</button>
</div>

Step 3: wire up SpeechRecognition

// File: assets/js/chat-widget.js
function initVoiceInput() {
	var SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
	var micButton = document.getElementById( 'site-chat-mic' );

	if ( ! SpeechRecognition ) {
		micButton.style.display = 'none'; // No graceful degradation needed, just hide it.
		return;
	}

	var recognition = new SpeechRecognition();
	recognition.lang = document.documentElement.lang || 'en-US';
	recognition.interimResults = false;

	micButton.addEventListener( 'click', function () {
		micButton.classList.add( 'site-chat-mic--listening' );
		recognition.start();
	} );

	recognition.addEventListener( 'result', function ( event ) {
		var transcript = event.results[ 0 ][ 0 ].transcript;
		document.getElementById( 'site-chat-input' ).value = transcript;
	} );

	recognition.addEventListener( 'end', function () {
		micButton.classList.remove( 'site-chat-mic--listening' );
	} );

	recognition.addEventListener( 'error', function () {
		micButton.classList.remove( 'site-chat-mic--listening' );
		appendMessage( 'assistant', 'Sorry, I couldn\'t hear that clearly. Try typing instead.' );
	} );
}

recognition.lang, read from document.documentElement.lang, the same locale value WordPress already writes onto the <html> tag, means a Spanish-language page gets Spanish speech recognition without any extra configuration. This lines up directly with Lesson 5’s multilingual handling: the transcript that lands in the text input goes through the exact same language-detection and translation path any typed message would.

Step 4: let the visitor review before sending

Filling the text input rather than sending immediately on a recognized result matters: speech recognition is genuinely imperfect, and a visitor should get to glance at the transcript and correct an obvious misrecognition before it becomes a message sent to the model. Wire the existing send button, unchanged from Course 11, to fire normally once the visitor is satisfied with what’s in the box.

Browser support for SpeechRecognition is real but uneven

SpeechRecognition (unprefixed) and webkitSpeechRecognition (the older, prefixed form Chrome and Chromium-based browsers still expose) cover the large majority of desktop and Android traffic, but support is not universal, and it can vary by browser version and platform. Checking window.SpeechRecognition || window.webkitSpeechRecognition before doing anything else, exactly as Step 3 does, and hiding the microphone button entirely when neither exists, is the whole mitigation. There is no polyfill worth reaching for here, a visitor on an unsupported browser simply types, exactly as before this lesson existed.

Step 5: confirm the backend truly needs nothing new

This is worth stating directly because it’s easy to assume otherwise: no REST route change, no new parameter, no PHP file in this lesson at all. sendMessage(), Course 11’s existing function, and my_plugin_handle_chat_request(), the existing callback, never change. Voice input is entirely upstream of both, a different way of populating the same text string sendMessage() already expected.

Test it: speak a question and confirm nothing downstream changed

In a supporting browser, click the microphone button, speak a short question, and confirm the recognized text appears in the input box for review. Send it, and confirm in the network tab that the request to /wp-json/my-plugin/v1/chat is byte-for-byte identical in shape to one sent by typing, same message field, same session_id, nothing marking it as voice-originated. Then test in a browser without support (or with JavaScript’s SpeechRecognition temporarily deleted from window in devtools) and confirm the microphone button is simply absent, with typing working exactly as before.

Recap

Voice input for this chatbot is entirely a front-end concern: the standard, browser-native Web Speech API converts spoken audio to text client-side, fills the same text input Course 11 already built, and lets the visitor review and edit before sending. Nothing about the REST endpoint, the session handling, the RAG pipeline, or the human takeover built in earlier lessons needed to change, because by the time any of that code runs, voice input has already become an ordinary string in the message field it always expected.

Resources & further reading

← Multilingual Support for the Chatbot Lead Capture Within the Chat Flow →