Speech-to-text sounds simple from the outside: user speaks, app shows text.
In JavaScript, the real choice is how you want that speech to become text. You can use the browser’s built-in speech recognition for quick dictation. You can record audio in the browser and send it to a transcription API. You can build a real-time voice app with streaming. Or you can send the transcript into an AI workflow that cleans it, summarizes it, classifies it, or stores it.
This guide walks through the practical paths, with code examples and simple advice on when each one makes sense.
Start with the use case
Before writing code, pick the speech-to-text path that matches the product.
| Use case | Best JavaScript path | Why |
| Quick browser dictation | Web Speech API | Fast to build, no backend needed |
| Voice note upload | MediaRecorder + transcription API | Better control and more reliable output |
| Meeting recorder | Record audio, send file to API | Easier than live streaming |
| Live captions | Streaming speech-to-text API | Lower latency and partial results |
| Voice agent | Realtime audio session | Needed when the app listens and responds |
| Private internal workflow | Backend transcription API | Keeps API keys and logs under control |
For a small demo, the browser-native route is fine. For a real product, recording audio and sending it to a transcription API is usually easier to manage.
Why we can write this guide
We have around 6 years of experience working with AI APIs, developer tools, automation workflows, and content systems. We also researched current browser APIs, speech-to-text providers, and automatic speech recognition research before writing this.
The main thing we found: speech-to-text quality depends on the full workflow. The model matters, but so do audio quality, microphone access, browser support, latency, file format, language, accents, background noise, and what you do with the transcript after.
The Whisper research paper is a useful example. It showed that large-scale weak supervision across 680,000 hours of multilingual audio can improve robustness across different speech conditions. For developers, the practical lesson is simple: modern transcription models are much better than older systems, but your app still needs good audio handling and smart fallback logic.
Path 1: Use the browser’s web speech API
The fastest way to convert speech to text with JavaScript is the Web Speech API.
MDN describes the Web Speech API as a browser API for speech recognition and speech synthesis. The recognition part is handled through SpeechRecognition.
This is good for:
| Good fit | Weak fit |
| Small demos | Production apps that need consistent browser support |
| Dictation fields | Sensitive audio workflows |
| Voice commands | Long recordings |
| Internal tools | Offline transcription |
There is one important catch. MDN marks SpeechRecognition as limited availability because it does not work in every major browser. MDN also notes that some browsers may send audio to a server-based recognition service, so you should review privacy needs before using it.
Here is a simple browser example.
<button id="start">Start speaking</button>
<button id="stop">Stop</button>
<p><strong>Live text:</strong></p>
<div id="output"></div>
<script>
const SpeechRecognition =
window.SpeechRecognition || window.webkitSpeechRecognition;
const output = document.getElementById("output");
const startButton = document.getElementById("start");
const stopButton = document.getElementById("stop");
if (!SpeechRecognition) {
output.textContent = "Speech recognition is not supported in this browser.";
} else {
const recognition = new SpeechRecognition();
recognition.lang = "en-US";
recognition.continuous = true;
recognition.interimResults = true;
recognition.onresult = (event) => {
let transcript = "";
for (let i = 0; i < event.results.length; i++) {
transcript += event.results[i][0].transcript;
}
output.textContent = transcript;
};
recognition.onerror = (event) => {
output.textContent = `Error: ${event.error}`;
};
startButton.addEventListener("click", () => {
recognition.start();
});
stopButton.addEventListener("click", () => {
recognition.stop();
});
}
</script>
This gives you a working speech-to-text demo in a few minutes. It is the easiest option for a tutorial, a prototype, or a local admin tool.
For a user-facing product, test it across your target browsers first. Browser support and recognition behavior can change the user experience a lot.
Path 2: Record audio in the browser
The next path is more flexible:
- Ask the user for microphone access.
- Record audio in the browser.
- Send the audio file to your backend.
- Transcribe it with an API.
- Return the transcript to the frontend.
This gives you more control than browser-native recognition.
The main browser API here is MediaRecorder. MDN describes it as part of the MediaStream Recording API, used to record media from a stream. MDN also notes that it has been broadly available across browsers since April 2021, though some details can vary by browser.
Here is a simple recorder.
<button id="record">Start recording</button>
<button id="stop" disabled>Stop recording</button>
<audio id="player" controls></audio>
<pre id="status"></pre>
<script>
const recordButton = document.getElementById("record");
const stopButton = document.getElementById("stop");
const player = document.getElementById("player");
const statusBox = document.getElementById("status");
let mediaRecorder;
let audioChunks = [];
recordButton.addEventListener("click", async () => {
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
mediaRecorder = new MediaRecorder(stream);
audioChunks = [];
mediaRecorder.addEventListener("dataavailable", (event) => {
audioChunks.push(event.data);
});
mediaRecorder.addEventListener("stop", async () => {
const audioBlob = new Blob(audioChunks, { type: "audio/webm" });
const audioUrl = URL.createObjectURL(audioBlob);
player.src = audioUrl;
statusBox.textContent = "Recording ready.";
await sendAudioToServer(audioBlob);
});
mediaRecorder.start();
recordButton.disabled = true;
stopButton.disabled = false;
statusBox.textContent = "Recording...";
});
stopButton.addEventListener("click", () => {
mediaRecorder.stop();
recordButton.disabled = false;
stopButton.disabled = true;
});
async function sendAudioToServer(audioBlob) {
const formData = new FormData();
formData.append("audio", audioBlob, "recording.webm");
const response = await fetch("/api/transcribe", {
method: "POST",
body: formData
});
const data = await response.json();
statusBox.textContent = data.text;
}
</script>
This creates an audio blob in the browser and sends it to /api/transcribe.
Keep API keys out of frontend code. The browser should record audio, then your backend should call the transcription provider.
Path 3: Transcribe the recording with OpenAI
OpenAI’s current speech-to-text docs list the transcriptions endpoint for converting audio into text. The docs also list supported upload formats including mp3, mp4, mpeg, mpga, m4a, wav, and webm, with a 25 MB file upload limit.
The docs currently show gpt-4o-transcribe, gpt-4o-mini-transcribe, and gpt-4o-transcribe-diarize as transcription model options. The diarization model is useful when you need speaker labels.
Here is a simple Node.js backend route.
npm install express multer openai
import express from "express";
import multer from "multer";
import fs from "node:fs";
import { unlink } from "node:fs/promises";
import OpenAI from "openai";
const app = express();
const upload = multer({ dest: "uploads/" });
const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY
});
app.post("/api/transcribe", upload.single("audio"), async (req, res) => {
try {
const transcription = await openai.audio.transcriptions.create({
file: fs.createReadStream(req.file.path),
model: "gpt-4o-transcribe"
});
await unlink(req.file.path);
res.json({ text: transcription.text });
} catch (error) {
if (req.file?.path) {
await unlink(req.file.path).catch(() => {});
}
res.status(500).json({
error: "Transcription failed"
});
}
});
app.listen(3000, () => {
console.log("Server running on http://localhost:3000");
});
This is the cleanest beginner-friendly architecture:
Browser microphone → MediaRecorder → Audio Blob → Backend upload route → Transcription API → Text returned to frontend
For many apps, this is enough.
Path 4: Use streaming for live transcription
File transcription works well for recordings. Streaming is better when the user needs live feedback.
OpenAI’s audio guide separates request-based audio APIs from realtime sessions. Request-based APIs are simpler for completed files. Realtime sessions are better when audio is live and the app needs low-latency events.
Deepgram’s speech-to-text docs describe a similar split: streaming audio for voice agents, realtime transcription for meetings and events, and pre-recorded audio for uploaded files.
Use streaming for:
| Use case | Why streaming helps |
| Live captions | Users see partial text quickly |
| Voice agents | The app can detect when the user stops speaking |
| Call center tools | Agents can see text during the call |
| Meeting assistants | Notes can appear while people talk |
| Accessibility tools | Low latency matters |
Use file transcription for:
| Use case | Why file transcription works |
| Voice notes | User can wait a few seconds |
| Podcasts | Accuracy matters more than instant output |
| Interviews | Post-processing is normal |
| Uploaded lectures | Long-form processing is expected |
| Internal reports | Batch workflows are easier |
A simple rule: if the user is watching the transcript appear, use streaming. If the user uploads or records something and waits, use file transcription.
Choosing the right speech-to-text setup
Here is the practical choice map.
| If you need… | Choose… |
| Fastest demo | Web Speech API |
| Better accuracy and control | MediaRecorder + transcription API |
| No backend | Web Speech API |
| Secure API key handling | Backend transcription route |
| Long recordings | File upload transcription |
| Live captions | Streaming API |
| Speaker labels | Diarization-capable model |
| Multilingual transcription | Modern STT model with language support |
| Voice agent | Realtime audio session |
| Transcript cleanup | STT + LLM post-processing |
The Conformer paper is useful background here because it showed how combining convolution and transformer layers can improve speech recognition. The practical takeaway: speech recognition models are designed to capture both local sound patterns and longer context. That is why modern APIs often do better when the audio has enough context, clean input, and domain hints.
Improving transcript quality
Bad audio makes every speech-to-text system worse. Good code helps, but the microphone and recording conditions matter too.
Use this checklist before blaming the model:
| Check | Why it matters |
| Use a decent microphone | Built-in laptop mics pick up room noise |
| Reduce background noise | Noise can create wrong words |
| Avoid overlapping speakers | Speaker overlap is hard to transcribe |
| Set the correct language | Helps names, spelling, and punctuation |
| Keep recordings focused | Shorter clips are easier to debug |
| Add domain context | Product names and acronyms need help |
| Use diarization for meetings | Speaker labels need model support |
| Store raw audio temporarily | Helps debug bad transcripts |
| Keep user consent clear | Voice data can be sensitive |
OpenAI’s speech-to-text docs mention that prompts can help transcription quality for specific words, acronyms, punctuation style, or segmented files. That is useful for apps with brand names, medical terms, legal terms, internal product names, or uncommon spellings.
Example:
const transcription = await openai.audio.transcriptions.create({
file: fs.createReadStream("sales-call.webm"),
model: "gpt-4o-transcribe",
prompt: "The transcript may mention LLMAPI, SOC 2, SSO, RAG, embeddings, and model routing."
});
This does not guarantee perfect spelling, but it gives the model useful context.
Add transcript cleanup
Raw transcripts are often messy. People pause, repeat themselves, change direction, and use filler words.
After transcription, you can send the text to an LLM for cleanup.
Common cleanup tasks include:
| Task | Example |
| Remove filler words | Clean meeting notes |
| Add punctuation | Make transcript readable |
| Summarize | Create short notes |
| Extract action items | Turn calls into tasks |
| Classify intent | Route support messages |
| Detect language | Store transcripts by language |
| Create titles | Name voice notes automatically |
| Generate chapters | Split podcasts or lectures |
A simple workflow can look like this:
Audio recording → Speech-to-text API → Raw transcript → LLM cleanup step → Summary, tags, action items, or structured data
This is where LLMAPI can fit well. You can use one workflow to transcribe the audio, then route the transcript to another model for summary, classification, moderation, translation, or formatting.
For example:
Customer call audio → Transcription → LLMAPI workflow → Summary + sentiment + action items → CRM record
That setup is useful for sales calls, support tickets, meeting assistants, training tools, voice notes, and internal search.
Privacy and security basics
Speech data can contain names, addresses, payment details, medical information, legal information, workplace details, or private conversations.
For a real app, use these rules:
| Rule | Why |
| Ask clearly before recording | Microphone access needs user consent |
| Keep API keys on the backend | Frontend keys can be copied |
| Delete temporary files | Audio files are sensitive |
| Review provider retention terms | Some apps need strict data rules |
| Log carefully | Transcripts can contain private data |
| Add upload limits | Prevents huge files and surprise costs |
| Show recording state | Users should know when the mic is active |
Also check local laws if you record calls or meetings. Consent rules vary by location and use case.
Full beginner workflow
Here is the simplest production-style version:
- User clicks “Record.”
- Browser asks for microphone access.
- MediaRecorder records audio.
- Browser sends recording.webm to your backend.
- Backend sends the file to a speech-to-text API.
- Backend returns the transcript.
- Optional LLM step cleans or summarizes the transcript.
- App displays or stores the final text.
That gives you a clean split:
| Layer | Responsibility |
| Browser | Record audio and show transcript |
| Backend | Protect API keys and call transcription provider |
| STT API | Convert audio to text |
| LLM layer | Clean, summarize, classify, or format |
| Database | Store transcript and metadata |
Common problems
SpeechRecognition is undefined
The browser does not support the Web Speech API.
Use this fallback:
const SpeechRecognition =
window.SpeechRecognition || window.webkitSpeechRecognition;
if (!SpeechRecognition) {
console.log("Use audio recording and backend transcription instead.");
}
The browser blocks microphone access
Use HTTPS in production. Browsers usually require secure contexts for microphone access.
The transcript is inaccurate
Check the audio first. Then check language, microphone quality, background noise, and domain vocabulary.
The uploaded file is too large
Compress audio, set recording limits, or split long recordings into smaller chunks. OpenAI’s speech-to-text docs currently list a 25 MB upload limit for file transcription.
The API key is exposed
Move transcription to the backend. The frontend should never call paid transcription APIs directly with a private key.
Final code structure
For a small app, this structure works well:
project/
server.js
package.json
public/
index.html
Frontend:
public/index.html
Records audio with MediaRecorder, then uploads it.
Backend:
server.js
Receives the file, sends it to the transcription API, returns JSON.
This keeps the build simple and avoids frontend key leaks.
Final thoughts
JavaScript gives you several good ways to convert speech to text.
Use the Web Speech API when you want a fast browser demo or simple dictation. Use MediaRecorder plus a transcription API when you want better control, stronger accuracy, and a setup that can grow into a real app. Use streaming when users need live captions, voice agents, or real-time call transcription.
The best path depends on the workflow. For a five-minute demo, browser recognition is enough. For a real product, record audio in the browser, send it to your backend, transcribe it with a reliable API, then use an LLM step to clean or structure the result.
That gives you a speech-to-text workflow that is simple, practical, and ready for useful features like summaries, action items, tags, search, and automation.