Blogs

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.


free-voice-generator-with-source-code.webp

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:

free-voice-generator-with-source-code.gif

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!

Leave a Reply

Your email address will not be published. Required fields are marked *

Back to top button
Hello! 👋

Start a Conversation

Our team is here to help you!

Arslan Gujjar

Arslan Gujjar

Support
Start Chat