Speech to Text in
Oracle APEX
What is This About?
I was working on an Oracle APEX application where users had to fill in a long notes field. Typing everything manually was getting annoying, especially on tablets. So I thought, why not add a voice input button that converts what the user says into text and puts it directly into the field?
Turns out, the browser already has this built in through something called the Web Speech API. No external libraries, no subscriptions, nothing to install. This post covers exactly how I got it working inside Oracle APEX using only a Static HTML Region and a small JavaScript block.
This approach uses the browser's native Web Speech API, which works in Chrome and Edge. Firefox does not support it by default, and Safari has partial support.
What We Are Building
The final result is a small voice input widget that sits next to any APEX text field. Here is a quick preview of what it looks like:
- A microphone button that starts voice recognition on click
- A language dropdown to switch between English US, India, or UK
- A "Listening..." indicator so the user knows it is recording
- The transcript gets appended directly into an APEX page item
The Path I Took
I first tried using a third party JavaScript library to handle speech recognition. It had good docs but it was pulling in way too many dependencies and did not integrate cleanly with APEX's dynamic actions. The page started throwing errors because of APEX's own jQuery version conflicts.
I tried external speech recognition libraries but it didn't work: loading an external speech library via APEX's JavaScript File URLs caused script loading order issues and multiple jQuery conflict errors in the browser console.
Then I came across the browser native window.SpeechRecognition API. No external files needed. It is already in Chrome. I just had to wrap it in a try-catch and attach it to a button click. That worked right away on the first test.
Finally this approach worked: using the browser's built-in Web Speech API directly inside Execute when Page Loads, with no extra dependencies at all.
One issue I faced was that the text was replacing what was already in the notes field instead of adding to it. Took me a while to figure out I needed to read the existing value using $v("P15_NOTES") first and then concatenate the new transcript before saving with $s().
Step by Step Setup
Create a Static HTML Region
In your APEX page, add a new region and set its type to Static Content. This is where the microphone button and language selector will live.
Paste the HTML Markup
Add the microphone button, language select dropdown, and the status div into the region's HTML Content source area.
Add JavaScript on Page Load
Go to Page Properties and paste the recognition logic inside the Execute when Page Loads section. No dynamic action needed.
Add the CSS Styles
Paste the CSS into the page's Inline CSS section or into the theme roller. This styles the button, dropdown, and status messages.
HTML Static Region Code
This is the HTML markup that goes inside the Static Content region. It creates the microphone button, the language selector and the status messages.
<button type="button" id="start-speech" class="t-Button t-Button--noLabel" title="Start Voice Input"> <span class="fa fa-microphone"></span> </button> <select id="speech-lang" class="t-Form-select"> <option value="en-US">English (US)</option> <option value="en-IN">English (India)</option> <option value="en-GB">English (UK)</option> </select> <div id="listening-msg" style="display:none;"> 🎤 Listening... </div> <div id="speech-status" style="display:none;"> <span class="fa fa-spinner fa-spin"></span> Voice recognition in progress... </div>
Execute When Page Loads
Paste this inside the page's Execute when Page Loads section. This attaches the speech recognition logic to the microphone button.
document.getElementById("start-speech").addEventListener("click", function () { try { const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition; if (!SpeechRecognition) { alert("Speech Recognition is not supported in this browser."); return; } const recognition = new SpeechRecognition(); const langSelect = document.getElementById("speech-lang"); recognition.lang = langSelect.value; recognition.interimResults = false; recognition.maxAlternatives = 1; document.getElementById("speech-status").style.display = "block"; document.getElementById("listening-msg").style.display = "block"; recognition.start(); recognition.onresult = function (event) { const speechResult = event.results[0][0].transcript; const currentText = $v("P15_NOTES"); $s("P15_NOTES", currentText + " " + speechResult); }; recognition.onerror = function (event) { console.error("Speech recognition error", event.error); alert("Error during speech recognition: " + event.error); }; recognition.onend = function () { document.getElementById("speech-status").style.display = "none"; document.getElementById("listening-msg").style.display = "none"; }; } catch (e) { console.error("Speech recognition not supported", e); } });
The key part here is reading the existing text with $v("P15_NOTES") before calling $s() to save. Without this, every new transcript would overwrite what was already typed.
Page Inline CSS
Add this to the Inline CSS section of your page. It styles the microphone button, the language dropdown, and the status messages so they fit nicely with the Universal Theme.
#speech-lang { padding: 6px 10px; border-radius: 8px; border: 1px solid #ccc; font-size: 14px; margin-top: 8px; margin-left: 8px; } #start-speech { background-color: #f0f4f7; border: 1px solid #ccc; border-radius: 50%; padding: 10px 12px; margin-right: 8px; cursor: pointer; transition: background-color 0.3s ease; } #start-speech:hover { background-color: #d0eaff; } #listening-msg { color: #0076d7; font-weight: bold; font-size: 14px; margin-top: 8px; display: flex; align-items: center; gap: 5px; } #speech-status { font-size: 13px; color: #555; margin-top: 4px; display: flex; align-items: center; gap: 6px; }
Things Worth Knowing
One issue I faced: the Web Speech API only works on HTTPS pages. If your APEX application is running on plain HTTP during development, the microphone permission dialog will never appear. Make sure you are on a secure connection.
Also, changing P15_NOTES to whatever your actual page item name is. That is the only thing you need to update if you are using a different page item.
If you want to support more languages, just add more <option> tags to the select with the correct BCP 47 language codes like hi-IN for Hindi or ta-IN for Tamil.
The recognition fires once per button click. If you want continuous listening, set recognition.continuous = true and handle the stop logic manually with a toggle button.
Wrapping Up
That is pretty much all there is to it. No plugins, no paid services. The whole thing runs entirely in the browser. If you are building an APEX app where users need to fill in long text fields, this is a really simple way to make the experience a lot better, especially on mobile or tablet.
The live demo link below shows exactly how it looks when running in a real APEX environment. The GitHub link has all the code in one place if you want to clone it.
Comments
Post a Comment