Answer the quiz by voice
Goal: on a kiosk or for a shopper who cannot tap, a spoken word picks an option and the quiz moves on; “skip” and “back” work too.
Uses: Storefront JS API, page.viewed, prefill(), navigate(), and the browser’s SpeechRecognition. Nothing to install; no API key. The methods work on every plan; page.viewed fires on the Plus and Enterprise plans.
Browser support, first
Section titled “Browser support, first”SpeechRecognition (prefixed webkitSpeechRecognition) exists in Chrome, Edge and Safari; Firefox does not ship it. Chrome and Edge send the audio to a cloud service, Safari to Apple’s, so it needs a network and a page served over HTTPS, and the microphone prompt only opens after a user gesture. The page below shows a “Talk to answer” button and leaves the quiz as it is where the API is missing.
Map words to options
Section titled “Map words to options”The quiz needs an option’s value, not the word the shopper said, so the page keeps a map per question: component_key in the editor to { spoken word: option value }. Recognition starts on each page.viewed and stops once a word matched.
<button id="talk" hidden>Talk to answer</button><script> window.octaneai = window.octaneai || []; window.octaneai.push(function (octaneai) { var Recognition = window.SpeechRecognition || window.webkitSpeechRecognition; if (!Recognition) return;
var WORDS = { 'image_choice-48su5': { dry: 'dry', oily: 'oily', combination: 'combination', normal: 'normal' }, 'multiple_choice-goals': { glow: ['glow'], hydration: ['hydration'], 'anti aging': ['anti_aging'] } }; var COMMANDS = { skip: 'skip', back: 'back', next: 'next' }; var listening = false; var button = document.getElementById('talk'); var recognition = new Recognition(); recognition.continuous = true; recognition.interimResults = false; recognition.lang = document.documentElement.lang || 'en-US';
recognition.onresult = function (e) { var heard = e.results[e.results.length - 1][0].transcript.trim().toLowerCase(); if (COMMANDS[heard]) { octaneai.navigate(COMMANDS[heard]); return; } Object.keys(WORDS).some(function (componentKey) { var word = Object.keys(WORDS[componentKey]).find(function (w) { return heard.indexOf(w) !== -1; }); if (!word) return false; var values = {}; values[componentKey] = WORDS[componentKey][word]; octaneai.prefill(values); octaneai.navigate('next'); return true; }); }; recognition.onend = function () { if (listening) recognition.start(); }; recognition.onerror = function (e) { if (e.error === 'not-allowed' || e.error === 'service-not-allowed') { listening = false; button.textContent = 'Microphone blocked'; } };
octaneai.ready(function () { button.hidden = false; }); button.addEventListener('click', function () { listening = !listening; button.textContent = listening ? 'Listening... (tap to stop)' : 'Talk to answer'; if (listening) recognition.start(); else recognition.stop(); }); octaneai.on('quiz.finished', function () { listening = false; recognition.stop(); }); });</script>prefill stages the option’s value into the current page; navigate('next') submits it right after, the same way a tap on the option and then on Next would. The match runs against every mapped question, so a word only answers the page it belongs to when the option values are distinct per question; use page_key from getState() to narrow the map when two pages share words.
Errors to handle
Section titled “Errors to handle”recognition.start()throwsInvalidStateErrorwhen it is already running; theonendrestart above only starts after a stop, so the toggle stays in step.onerrorwithnot-allowedmeans the microphone permission was refused;networkmeans the speech service is unreachable.no-speechandabortedare normal and restart throughonend.- A prefilled value the question does not have is refused when the page is submitted (
page.rejected,reason: 'invalid'), so a wrong entry inWORDSshows as a validation message, not a crash. navigate('next')on a page with an unanswered required question firespage.rejectedand stays; say “skip” to move on without answering.
Things to know
Section titled “Things to know”- Text, email and phone questions are not in the map on purpose: dictating an email address in a shop is neither reliable nor private. Tap those.
- Speech recognition is per tab; a quiz opened in a new tab needs its own button press.
- A multi-select question takes an array (
['glow']above, for one pick); map a phrase to['glow', 'hydration']andprefillstages both. A single-select takes the bare option value. - A kiosk that should listen without a press each time can keep
listeningtrue across page loads insessionStorage, but the firststart()after a load still needs a gesture in every browser.
