Voice
Shoppers can speak their question instead of typing, and the assistant can optionally read its replies aloud. Both controls are off by default, appear only in browsers that support them, and run entirely in the browser, so no audio is ever recorded or sent anywhere by the plugin.
What it does
When voice is on, a microphone button sits next to the message box. Pressing it dictates speech straight into the box using your browser's built in speech recognition, and a successfully dictated message is sent automatically. A second, optional control adds a speaker button so the assistant can read its answers aloud.
Typing always works. Voice is an additional way in, never the only way in, so nothing about the chat depends on a shopper having a microphone or a supported browser.
Two opt-in toggles
Voice is governed by two independent merchant settings under WooCommerce, then Fahad AI, in the Voice section. Both default to off.
- Voice Input is the master switch. It shows the microphone button so shoppers can speak their message. With it off, the widget receives no voice config at all, so it cannot build the mic or speaker controls and never prompts for a microphone permission.
- Spoken replies is a sub toggle that also lets the assistant read its replies aloud, with a speaker button to turn it on. It requires Voice Input and is meaningful only when the master switch is on. A merchant who wants hands free input without the assistant talking back can leave this off.
The settings map to two stored options:
fahad_ai_voice_enabled // master switch, default off
fahad_ai_voice_tts // spoken replies, default off
Two gates: setting and browser support
Every control passes through two gates before it appears. First the merchant setting above, enforced on the server: the widget is handed a voice config only when Voice Input is on. Second, browser support, checked in the widget itself.
- The microphone button is built only when the browser exposes
SpeechRecognitionorwebkitSpeechRecognition. If neither exists, no mic button is rendered at all and typing still works fully. - The speaker button is built only when spoken replies are enabled and the browser exposes
speechSynthesisandSpeechSynthesisUtterance. Otherwise the assistant simply never speaks.
Nothing leaves the browser
Recognition turns speech into text, and synthesis turns text into speech, both in the browser using the Web Speech APIs. The plugin adds no external service and never receives, processes, or stores any audio. The voice config it hands the widget is tiny and non identifying: whether voice is on, whether to speak replies, and the store language tag so recognition and synthesis match your store.
The microphone permission is always the browser's to grant. The widget calls recognition.start() and lets the browser prompt the shopper; it never bypasses that permission. If access is blocked, the shopper hears nothing intrusive and is quietly told they can still type.
Accessible and reduced-motion aware
The voice controls are built to be operable and clearly labelled for assistive technology, not just for sighted, pointer using shoppers.
- The mic and speaker are toggle buttons with
aria-pressed, so screen readers announce the recording and speaking state. - Status, such as listening or a blocked microphone, is announced through a polite live region rather than only shown visually.
- The on state is conveyed by an accent fill and the pressed state together, never by colour alone.
- The gentle pulse on the recording button is purely decorative and runs only under
prefers-reduced-motion: no-preference, with a scoped catch all that neutralises stray motion for shoppers who ask to reduce it.
Adjusting the config
The language tag comes from your site language, for example en-US, so recognition and synthesis match the store. If the site language is blank, the browser chooses its own default. A filter lets a site adjust the resolved config, for example to force a specific recognition locale, or to return an empty array to switch voice off for a given request:
add_filter( 'fahad_ai_voice_config', function ( $config ) {
// Force a recognition and synthesis locale.
$config['lang'] = 'en-GB';
return $config;
} );
A non array return is treated as no voice rather than corrupting the data handed to the widget. See Hooks and filters for the full list of extension points.