Free Voice Generator with Source Code
Build a free multi-language voice generator using HTML, CSS, and JavaScript. Step-by-step guide with source code for beginners.

Read AlsoCreate a Responsive Product Showcase Carousel using HTML, CSS, and JavaScript
Table of Contents
Voice technology is everywhere – from Google Translate to Alexa. If you’re learning web development, building a multi-language free voice generator is a fun and easy project. With only HTML, CSS, and JavaScript, you can create a text-to-speech tool that reads aloud any text you type.
In this blog, we’ll give you the source code and explain step by step how to build a voice generator that supports multiple languages.
Prerequisites
Before starting, make sure you have:
- Basic knowledge of HTML, CSS, and JavaScript
- A code editor like VS Code
- A modern web browser (Chrome, Edge, or Firefox)
Source Code
Step 1 (HTML Code):
First, we start with HTML. HTML helps us create the basic structure of the website.
- We create a container to hold everything.
- Inside it, we add a heading, a textarea where users can type text, dropdowns for selecting language and voice, and a button to start or stop speech.
- We also added a small debug panel to display logs and errors.
This step ensures that the website layout is clean, simple, and user-friendly.
1. Head Section
<!DOCTYPE html>
<html lang="en">
<!DOCTYPE html>→ Declares the document type as HTML5.<html lang="en">→ The webpage language is set to English (en). This helps browsers and search engines.
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Voice Generator</title>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
<link rel="stylesheet" href="styles.css">
</head>
<meta charset="UTF-8">→ Ensures the webpage supports all text characters (like English, Hindi, Emojis).<meta name="viewport"...>→ Makes the page responsive for mobile and desktop.<title>→ The title of the page shown in browser tabs (“Voice Generator”).<link href="https://fonts.googleapis.com/...">→ Imports the Google Font (Inter) for better design.<link rel="stylesheet" href="styles.css">→ Connects the external CSS file where all styling rules are written.
2. Container
<body>
<div class="container">
<h1>🎙️ Multi-Language Voice Generator</h1>
<body>→ The main visible content of the page.<div class="container">→ A wrapper that groups the entire app content for styling.<h1>→ A heading displaying the title of the app with a mic emoji 🎙️.
3. Text Area
<textarea id="text-input" placeholder="Enter text to speak here..."></textarea>
<textarea>→ A box where the user can type text.- id=”text-input” → Gives it a unique ID so JavaScript can fetch the input.
- placeholder=”…” → Shows hint text inside the box until the user types.
4. Language & Voice Dropdowns
<div class="controls">
<div class="control-group">
<label for="language-select">Select Language</label>
<select id="language-select"></select>
</div>
<div class="control-group">
<label for="voice-select">Select Voice</label>
<select id="voice-select"></select>
</div>
</div>
<div class="controls">→ A section that groups all settings.<label>→ Describes the input field (good for accessibility).<select>→ A dropdown menu.- id=”language-select” → Dropdown for choosing language (like English, Hindi, French).
- id=”voice-select” → Dropdown for choosing a voice style (like male/female voices).
- These
<select>elements are empty now but will be filled dynamically with options using JavaScript.
5. Speak Button
<button id="speak-button" disabled>Speak</button>
<button>→ A button that will trigger speech.- id=”speak-button” → JavaScript can attach a click event here.
- disabled → The button starts inactive until voices are loaded by JavaScript.
6. Debug Panel
<div class="debug-panel">
<div id="log-output"></div>
</div>
<div class="debug-panel">→ A section for debugging messages.<div id="log-output">→ Will display logs, errors, or status messages added by JavaScript.
7. JavaScript File
<script src="script.js"></script>
- Connects the JavaScript file (script.js) where all the functionality (voice generation, dropdown population, and speech synthesis) is written.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Voice Generator</title>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div class="container">
<h1>🎙️ Multi-Language Voice Generator</h1>
<textarea id="text-input" placeholder="Enter text to speak here..."></textarea>
<div class="controls">
<div class="control-group">
<label for="language-select">Select Language</label>
<select id="language-select"></select>
</div>
<div class="control-group">
<label for="voice-select">Select Voice</label>
<select id="voice-select"></select>
</div>
</div>
<button id="speak-button" disabled>Speak</button>
<div class="debug-panel">
<div id="log-output"></div>
</div>
</div>
<script src="script.js"></script>
</body>
</html>
Read AlsoCreate a Responsive Customer Review Using HTML and CSS
Step 2 (CSS Code):
https://googleads.g.doubleclick.net/pagead/ads?gdpr=0&us_privacy=1—&gpp_sid=-1&client=ca-pub-3303804766726108&output=html&h=280&adk=4053954489&adf=4265404821&w=608&fwrn=4&fwrnh=100&lmt=1760940255&num_ads=1&rafmt=1&armr=3&sem=mc&pwprc=2548869305&ad_type=text_image&format=608×280&url=https%3A%2F%2Fwww.codewithfaraz.com%2Fcontent%2F553%2Ffree-voice-generator-with-source-code%23google_vignette&host=ca-host-pub-3303804766726108&fwr=0&pra=3&rh=152&rw=608&rpe=1&resp_fmts=3&wgl=1&fa=27&uach=WyJXaW5kb3dzIiwiMTAuMC4wIiwieDg2IiwiIiwiMTQxLjAuNzM5MC4xMDgiLG51bGwsMCxudWxsLCI2NCIsW1siR29vZ2xlIENocm9tZSIsIjE0MS4wLjczOTAuMTA4Il0sWyJOb3Q_QV9CcmFuZCIsIjguMC4wLjAiXSxbIkNocm9taXVtIiwiMTQxLjAuNzM5MC4xMDgiXV0sMF0.&abgtt=6&dt=1760940227976&bpp=2&bdt=1860&idt=2&shv=r20251016&mjsv=m202510150101&ptt=9&saldr=aa&abxe=1&cookie=ID%3Dbe1ed40d2a9359e4%3AT%3D1759135297%3ART%3D1760940213%3AS%3DALNI_MZk9D2vKWr9dV-cAA41ODvEwo4qpA&gpic=UID%3D00001291f49e51c5%3AT%3D1759135297%3ART%3D1760940213%3AS%3DALNI_MZaxNU7tz3y9ltq7GBndMeNx9TwNg&eo_id_str=ID%3Db168cce45a342237%3AT%3D1759135297%3ART%3D1760940213%3AS%3DAA-AfjYoSLr7osKd_iUCcDnNOPXb&prev_fmts=0x0%2C608x280%2C608x280%2C737x674%2C552x280&nras=6&correlator=3022933186900&frm=20&pv=1&u_tz=300&u_his=6&u_h=768&u_w=1366&u_ah=728&u_aw=1366&u_cd=24&u_sd=0.9&dmc=4&adx=57&ady=5620&biw=721&bih=674&scr_x=0&scr_y=3181&eid=95373554%2C31095078%2C31095151%2C31095217%2C31095301%2C95373012%2C95374042%2C31095265%2C95372614%2C95368427%2C95368093&oid=2&pvsid=1096042265568456&tmod=375704122&uas=1&nvt=2&fc=1408&brdim=0%2C0%2C0%2C0%2C1366%2C0%2C1366%2C728%2C737%2C674&vis=1&rsz=%7C%7Cs%7C&abl=NS&fu=128&bc=31&bz=1.85&td=1&tdf=2&psd=W251bGwsbnVsbCxudWxsLDNd&nt=1&ifi=5&uci=a!5&btvi=4&fsb=1&dtd=27865
After building the structure, we move to CSS styling. CSS helps make the website look professional and modern.
- We set a nice background color and center the layout.
- The container is styled with rounded corners and a shadow effect to look like a card.
- Input fields (like textarea, select, and button) are given padding, borders, and hover effects.
- We also style the debug panel with a dark background, colored log messages (error, success, info), and auto-scroll for better readability.
This step gives our project a beautiful, responsive design that works well on different screen sizes.
1. Global Styles
* {
font-family: 'Inter', sans-serif;
box-sizing: border-box;
margin: 0;
padding: 0;
}
- * → Applies to all elements.
- font-family: ‘Inter’ → Uses the Google Inter font everywhere.
- box-sizing: border-box → Includes padding and borders inside width/height (makes layout easier).
- margin: 0; padding: 0; → Removes default spacing from all elements.
2. Body Styles
body {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background-color: #f0f2f5;
color: #333;
padding: 20px;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
- display: flex; justify-content: center; align-items: center; → Centers everything both vertically and horizontally.
- min-height: 100vh; → Takes at least the full viewport height.
- background-color: #f0f2f5; → Light gray background.
- color: #333; → Default text color (dark gray).
- padding: 20px; → Prevents content from touching edges.
- font-smoothing → Makes text look sharper and smoother.
3. Container
.container {
background: white;
padding: 1.5rem;
border-radius: 12px;
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12);
width: 90%;
max-width: 650px;
}
- White card-like box in the center.
- border-radius: 12px; → Rounded corners.
- box-shadow → Soft shadow for a modern look.
- max-width: 650px; → Keeps it readable on large screens.
4. Heading
h1 {
color: #0056b3;
text-align: center;
margin-bottom: 1rem;
}
- Blue heading, centered, with spacing below.
5. Textarea
textarea {
width: 100%;
height: 120px;
padding: 1rem;
border-radius: 8px;
border: 1px solid #ccc;
font-size: 1rem;
margin-bottom: 1rem;
box-sizing: border-box;
resize: vertical;
}
- Large typing box with rounded corners.
- resize: vertical; → User can resize only up & down.
textarea:focus,
select:focus {
outline: none;
border-color: #5a67d8;
box-shadow: 0 0 0 3px #a3bffa;
}
- When focused (clicked), the border turns blue and a glow effect is added.
6. Controls (Language & Voice Dropdowns)
.controls {
display: flex;
gap: 1rem;
margin-bottom: 1.5rem;
}
.control-group {
flex: 1;
display: flex;
flex-direction: column;
}
- .controls → Places language and voice dropdowns side by side.
- .control-group → Makes each dropdown fill equal space, stacked with its label.
label {
margin-bottom: 0.5rem;
font-weight: 600;
text-align: left;
font-size: 0.9rem;
color: #555;
}
- Labels above dropdowns with darker text and spacing.
7. Dropdowns & Buttons
select,
button {
width: 100%;
padding: 0.8rem;
font-size: 1rem;
border-radius: 8px;
border: 1px solid #ccc;
cursor: pointer;
background-color: #fff;
}
- Shared styles for select menus and button: full width, rounded, clickable.
button {
background-color: #007bff;
color: white;
border: none;
font-weight: bold;
transition: background-color 0.3s;
}
button:hover:not(:disabled) {
background-color: #0056b3;
}
button:disabled {
background-color: #aaa;
cursor: not-allowed;
}
- Blue button with smooth hover effect.
- The disabled button turns gray and cannot be clicked.
8. Debug Panel
.debug-panel {
margin-top: 2rem;
padding: 1rem;
background-color: #2d3436;
color: #dfe6e9;
border-radius: 8px;
text-align: left;
font-family: 'Courier New', Courier, monospace;
font-size: 0.85rem;
max-height: 150px;
overflow-y: auto;
border: 1px solid #444;
}
- Dark panel at the bottom for showing logs.
- Monospace font (like coding console).
- overflow-y: auto; → Adds a scrollbar if too many logs.
.debug-panel p {
margin: 0 0 5px 0;
padding: 0 0 5px 0;
border-bottom: 1px dotted #555;
word-wrap: break-word;
}
- Each log entry has spacing, a bottom dotted line, and wraps text.
9. Log Colors
.log-error {
color: #ff7675;
font-weight: bold;
}
.log-success {
color: #55efc4;
font-weight: bold;
}
.log-info {
color: #74b9ff;
}
- .log-error → Red text for errors.
- .log-success → Green text for success.
- .log-info → Blue text for normal messages.
* {
font-family: 'Inter', sans-serif;
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background-color: #f0f2f5;
color: #333;
padding: 20px;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.container {
background: white;
padding: 1.5rem;
border-radius: 12px;
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12);
width: 90%;
max-width: 650px;
}
h1 {
color: #0056b3;
text-align: center;
margin-bottom: 1rem;
}
textarea {
width: 100%;
height: 120px;
padding: 1rem;
border-radius: 8px;
border: 1px solid #ccc;
font-size: 1rem;
margin-bottom: 1rem;
box-sizing: border-box;
resize: vertical;
}
textarea:focus,
select:focus {
outline: none;
border-color: #5a67d8;
box-shadow: 0 0 0 3px #a3bffa;
}
.controls {
display: flex;
gap: 1rem;
margin-bottom: 1.5rem;
}
.control-group {
flex: 1;
display: flex;
flex-direction: column;
}
label {
margin-bottom: 0.5rem;
font-weight: 600;
text-align: left;
font-size: 0.9rem;
color: #555;
}
select,
button {
width: 100%;
padding: 0.8rem;
font-size: 1rem;
border-radius: 8px;
border: 1px solid #ccc;
cursor: pointer;
background-color: #fff;
}
button {
background-color: #007bff;
color: white;
border: none;
font-weight: bold;
transition: background-color 0.3s;
}
button:hover:not(:disabled) {
background-color: #0056b3;
}
button:disabled {
background-color: #aaa;
cursor: not-allowed;
}
.debug-panel {
margin-top: 2rem;
padding: 1rem;
background-color: #2d3436;
color: #dfe6e9;
border-radius: 8px;
text-align: left;
font-family: 'Courier New', Courier, monospace;
font-size: 0.85rem;
max-height: 150px;
overflow-y: auto;
border: 1px solid #444;
}
.debug-panel p {
margin: 0 0 5px 0;
padding: 0 0 5px 0;
border-bottom: 1px dotted #555;
word-wrap: break-word;
}
.log-error {
color: #ff7675;
font-weight: bold;
}
.log-success {
color: #55efc4;
font-weight: bold;
}
.log-info {
color: #74b9ff;
}
Step 3 (JavaScript Code):
https://googleads.g.doubleclick.net/pagead/ads?gdpr=0&us_privacy=1—&gpp_sid=-1&client=ca-pub-3303804766726108&output=html&h=280&adk=4053954489&adf=1915238605&w=608&fwrn=4&fwrnh=100&lmt=1760940255&num_ads=1&rafmt=1&armr=3&sem=mc&pwprc=2548869305&ad_type=text_image&format=608×280&url=https%3A%2F%2Fwww.codewithfaraz.com%2Fcontent%2F553%2Ffree-voice-generator-with-source-code%23google_vignette&host=ca-host-pub-3303804766726108&fwr=0&pra=3&rh=152&rw=608&rpe=1&resp_fmts=3&wgl=1&fa=27&uach=WyJXaW5kb3dzIiwiMTAuMC4wIiwieDg2IiwiIiwiMTQxLjAuNzM5MC4xMDgiLG51bGwsMCxudWxsLCI2NCIsW1siR29vZ2xlIENocm9tZSIsIjE0MS4wLjczOTAuMTA4Il0sWyJOb3Q_QV9CcmFuZCIsIjguMC4wLjAiXSxbIkNocm9taXVtIiwiMTQxLjAuNzM5MC4xMDgiXV0sMF0.&abgtt=6&dt=1760940228023&bpp=3&bdt=1907&idt=3&shv=r20251016&mjsv=m202510150101&ptt=9&saldr=aa&abxe=1&cookie=ID%3Dbe1ed40d2a9359e4%3AT%3D1759135297%3ART%3D1760940213%3AS%3DALNI_MZk9D2vKWr9dV-cAA41ODvEwo4qpA&gpic=UID%3D00001291f49e51c5%3AT%3D1759135297%3ART%3D1760940213%3AS%3DALNI_MZaxNU7tz3y9ltq7GBndMeNx9TwNg&eo_id_str=ID%3Db168cce45a342237%3AT%3D1759135297%3ART%3D1760940213%3AS%3DAA-AfjYoSLr7osKd_iUCcDnNOPXb&prev_fmts=0x0%2C608x280%2C608x280%2C737x674%2C552x280%2C608x280&nras=7&correlator=3022933186900&frm=20&pv=1&u_tz=300&u_his=6&u_h=768&u_w=1366&u_ah=728&u_aw=1366&u_cd=24&u_sd=0.9&dmc=4&adx=57&ady=12262&biw=721&bih=674&scr_x=0&scr_y=9716&eid=95373554%2C31095078%2C31095151%2C31095217%2C31095301%2C95373012%2C95374042%2C31095265%2C95372614%2C95368427%2C95368093&oid=2&pvsid=1096042265568456&tmod=375704122&uas=1&nvt=2&fc=1408&brdim=0%2C0%2C0%2C0%2C1366%2C0%2C1366%2C728%2C737%2C674&vis=1&rsz=%7C%7Cs%7C&abl=NS&fu=128&bc=31&bz=1.85&td=1&tdf=2&psd=W251bGwsbnVsbCxudWxsLDNd&nt=1&ifi=6&uci=a!6&btvi=5&fsb=1&dtd=27944
Finally, we use JavaScript to add life to our project.
- With the Web Speech API, we load all available voices and languages from the browser.
- When the user selects a language, the script updates the voice dropdown automatically.
- Clicking the Speak button will read aloud the text typed in the textarea.
- We also added a Stop feature, error handling, and detailed logs in the debug panel.
This step makes the website fully functional as a Multi-Language Voice Generator.
1. Setup
document.addEventListener('DOMContentLoaded', () => {
- Runs the script only after the page has fully loaded.
const synth = window.speechSynthesis;
- Gets the speech synthesis engine from the browser.
const textInput = document.getElementById('text-input');
const languageSelect = document.getElementById('language-select');
const voiceSelect = document.getElementById('voice-select');
const speakButton = document.getElementById('speak-button');
const logOutput = document.getElementById('log-output');
- Connects HTML elements (textarea, dropdowns, button, debug panel) to JavaScript.
let voices = [];
- Stores available voices from the browser.
2. Logging System
function log(message, type = 'info') {
console.log(message);
const p = document.createElement('p');
p.textContent = `> ${message}`;
p.classList.add(`log-${type}`);
logOutput.appendChild(p);
logOutput.scrollTop = logOutput.scrollHeight;
}
- Prints logs to both the console and the debug panel.
- Adds styling classes like .log-error, .log-success, .log-info.
- Auto-scrolls down to show the latest log.
3. Detect Voice Gender (optional feature)
function getGenderFromName(name) {
const lowerCaseName = name.toLowerCase();
const maleNames = ['ravi', 'male', 'man', 'rishi'];
const femaleNames = ['heera','female','woman','lekha','kalpana','zira','susan'];
if (maleNames.some((maleName) => lowerCaseName.includes(maleName)))
return '(Male)';
if (femaleNames.some((femaleName) => lowerCaseName.includes(femaleName)))
return '(Female)';
return '';
}
- Tries to guess the gender of a voice by checking keywords in the voice’s name.
- Example: “Google US English (Female)” → shows (Female) next to it.
4. Populate Voice Dropdown
function populateVoiceList(language) {
const voicesForLanguage = voices.filter((voice) => voice.lang === language);
voiceSelect.innerHTML = '';
- Filters voices by selected language.
voicesForLanguage.forEach((voice) => {
const option = document.createElement('option');
const gender = getGenderFromName(voice.name);
option.textContent = `${voice.name} ${gender}`;
option.setAttribute('data-name', voice.name);
voiceSelect.appendChild(option);
});
- Creates
<option>for each available voice. - Shows voice name + gender.
log(`Populated ${voicesForLanguage.length} voices for language: ${language}.`);
- Logs how many voices were found.
- If none → shows error.
5. Initialize (Load Voices & Languages)
function initialize() {
voices = synth.getVoices();
- Fetches all voices from the browser.
if (voices.length === 0) {
log('No voices loaded yet. Retrying in a moment...', 'error');
setTimeout(initialize, 100);
return;
}
- If voices are not ready, retry every 100ms (some browsers load slowly).
const languages = [...new Set(voices.map((voice) => voice.lang))];
languageSelect.innerHTML = '';
- Gets unique language codes (e.g., en-US, hi-IN, fr-FR).
const langNames = new Intl.DisplayNames(['en'], { type: 'language' });
- Converts language codes into human-friendly names like “English (United States)”.
languages.forEach((lang) => {
const option = document.createElement('option');
option.value = lang;
try {
const regionNames = new Intl.DisplayNames(['en'], { type: 'region' });
const parts = lang.split('-');
const langName = langNames.of(parts[0]);
const regionName = parts[1] ? ` (${regionNames.of(parts[1])})` : '';
option.textContent = `${langName}${regionName}`;
} catch (e) {
option.textContent = lang;
}
languageSelect.appendChild(option);
});
- Builds dropdown options like:
- en-US → English (United States)
- hi-IN → Hindi (India)
const defaultLang = languages.includes('hi-IN') ? 'hi-IN' : languages[0];
languageSelect.value = defaultLang;
populateVoiceList(defaultLang);
- Defaults to Hindi (hi-IN) if available, else picks the first language.
speakButton.disabled = false;
log('Generator is ready!', 'success');
- Enables the Speak button and logs success.
6. Speak Function
function speak() {
if (synth.speaking) {
synth.cancel();
return;
}
- If already speaking → stop (button works like toggle: Speak/Stop).
if (textInput.value !== '') {
const utterThis = new SpeechSynthesisUtterance(textInput.value);
const selectedVoiceName = voiceSelect.selectedOptions[0]?.getAttribute('data-name');
- Creates a new speech object with the typed text.
- Gets the selected voice from the dropdown.
utterThis.voice = voices.find((voice) => voice.name === selectedVoiceName);
utterThis.lang = languageSelect.value;
- Applies chosen voice and language.
utterThis.onstart = () => { speakButton.textContent = 'Stop'; };
utterThis.onend = () => { speakButton.textContent = 'Speak'; };
utterThis.onerror = (e) => {
log(`An error occurred: ${e.error}`, 'error');
speakButton.textContent = 'Speak';
};
- Updates button text while speaking.
- Handles errors gracefully.
synth.speak(utterThis);
- Speaks the text aloud 🎙️.
7. Web Speech API Support Check
if ('speechSynthesis' in window) {
log('Web Speech API is supported.', 'success');
synth.onvoiceschanged = initialize;
initialize();
} else {
log('Error: Web Speech API is not supported by this browser.', 'error');
speakButton.disabled = true;
speakButton.textContent = 'Not Supported';
}
- Checks if the browser supports speech synthesis.
- If yes → load voices.
- If not → disable the Speak button.
8. Event Listeners
languageSelect.addEventListener('change', () => {
populateVoiceList(languageSelect.value);
});
speakButton.addEventListener('click', speak);
- When user changes language → reload available voices.
- When user clicks button → speak the text.
document.addEventListener('DOMContentLoaded', () => {
const synth = window.speechSynthesis;
const textInput = document.getElementById('text-input');
const languageSelect = document.getElementById('language-select');
const voiceSelect = document.getElementById('voice-select');
const speakButton = document.getElementById('speak-button');
const logOutput = document.getElementById('log-output');
let voices = [];
function log(message, type = 'info') {
console.log(message);
const p = document.createElement('p');
p.textContent = `> ${message}`;
p.classList.add(`log-${type}`);
logOutput.appendChild(p);
logOutput.scrollTop = logOutput.scrollHeight;
}
function getGenderFromName(name) {
const lowerCaseName = name.toLowerCase();
const maleNames = ['ravi', 'male', 'man', 'rishi'];
const femaleNames = [
'heera',
'female',
'woman',
'lekha',
'kalpana',
'zira',
'susan',
];
if (maleNames.some((maleName) => lowerCaseName.includes(maleName)))
return '(Male)';
if (femaleNames.some((femaleName) => lowerCaseName.includes(femaleName)))
return '(Female)';
return '';
}
function populateVoiceList(language) {
const voicesForLanguage = voices.filter((voice) => voice.lang === language);
voiceSelect.innerHTML = '';
if (voicesForLanguage.length > 0) {
voicesForLanguage.forEach((voice) => {
const option = document.createElement('option');
const gender = getGenderFromName(voice.name);
option.textContent = `${voice.name} ${gender}`;
option.setAttribute('data-name', voice.name);
voiceSelect.appendChild(option);
});
log(
`Populated ${voicesForLanguage.length} voices for language: ${language}.`
);
} else {
log(`No voices found for language: ${language}.`, 'error');
}
}
function initialize() {
voices = synth.getVoices();
if (voices.length === 0) {
log('No voices loaded yet. Retrying in a moment...', 'error');
setTimeout(initialize, 100); // Retry if voices aren't loaded immediately
return;
}
log(`Found ${voices.length} total voices.`, 'success');
const languages = [...new Set(voices.map((voice) => voice.lang))];
languageSelect.innerHTML = '';
const langNames = new Intl.DisplayNames(['en'], { type: 'language' });
languages.forEach((lang) => {
const option = document.createElement('option');
option.value = lang;
// Format 'en-US' to 'English (United States)'
try {
const regionNames = new Intl.DisplayNames(['en'], { type: 'region' });
const parts = lang.split('-');
const langName = langNames.of(parts[0]);
const regionName = parts[1] ? ` (${regionNames.of(parts[1])})` : '';
option.textContent = `${langName}${regionName}`;
} catch (e) {
option.textContent = lang; // Fallback for complex tags
}
languageSelect.appendChild(option);
});
// Set default to Hindi if available, otherwise the first language
const defaultLang = languages.includes('hi-IN') ? 'hi-IN' : languages[0];
languageSelect.value = defaultLang;
populateVoiceList(defaultLang);
speakButton.disabled = false;
log('Generator is ready!', 'success');
}
function speak() {
if (synth.speaking) {
synth.cancel(); // If speaking, the button becomes a 'Stop' button
return;
}
if (textInput.value !== '') {
const utterThis = new SpeechSynthesisUtterance(textInput.value);
const selectedVoiceName =
voiceSelect.selectedOptions[0]?.getAttribute('data-name');
if (!selectedVoiceName) {
log('Error: No voice selected.', 'error');
return;
}
utterThis.voice = voices.find(
(voice) => voice.name === selectedVoiceName
);
utterThis.lang = languageSelect.value;
utterThis.onstart = () => {
speakButton.textContent = 'Stop';
};
utterThis.onend = () => {
speakButton.textContent = 'Speak';
};
utterThis.onerror = (e) => {
log(`An error occurred: ${e.error}`, 'error');
speakButton.textContent = 'Speak';
};
synth.speak(utterThis);
}
}
if ('speechSynthesis' in window) {
log('Web Speech API is supported.', 'success');
// The 'voiceschanged' event is the primary trigger for loading voices.
synth.onvoiceschanged = initialize;
// Call initialize directly as a fallback for some browsers.
initialize();
} else {
log('Error: Web Speech API is not supported by this browser.', 'error');
speakButton.disabled = true;
speakButton.textContent = 'Not Supported';
}
languageSelect.addEventListener('change', () => {
populateVoiceList(languageSelect.value);
});
speakButton.addEventListener('click', speak);
});
Final Output:

Read Also Blogging Website Landing Page using HTML, CSS, and JavaScript
Conclusion:
You just built a free multi-language voice generator with source code using HTML, CSS, and JavaScript 🎉. This project is simple, useful, and a great way to learn about the Web Speech API in JavaScript.
With this tool, you can:
- Convert text into speech instantly
- Choose from different languages like English, Hindi, French, Spanish, and more
- Expand it with features like speed, pitch, and voice style
👉 Try it out, improve it, and make your own unique text-to-speech project!
View Live Preview ⟶Download Source Code ⟶
That’s a wrap!
I hope you enjoyed this post. Now, with these examples, you can create your own amazing page.
Did you like it? Let me know in the comments below 🔥 and you can support me by buying me a coffee
And don’t forget to sign up to our email newsletter so you can get useful content like this sent right to your inbox!




