Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Chrome issue and format things for better understanding #4

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
# Text to Speech Using Web Speech API in JavaScript

If you'd like to learn how to build this application, refer to [this article](https://zolomohan.hashnode.dev/text-to-speech-using-the-web-speech-api-in-javascript).

## Browser Support

This should work fine on the current versions of Firefox, Chrome and Edge.

Chromium and Electron must be started with the argument `--enable-speech-dispatcher`.
82 changes: 47 additions & 35 deletions index.html
Original file line number Diff line number Diff line change
@@ -1,38 +1,50 @@
<html lang="en">
<head>
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" />
<link rel="stylesheet" href="index.css" />
<title>Text to Speech</title>
</head>
<body class="container mt-5 bg-dark">
<h1 class="text-light">Text to Speech</h1>
<p class="lead text-light mt-4">Select Voice</p>
<select id="voices" class="form-select bg-secondary text-light"></select>
<div class="d-flex mt-4 text-light">
<div>
<p class="lead">Volume</p>
<input type="range" min="0" max="1" value="1" step="0.1" id="volume" />
<span id="volume-label" class="ms-2">1</span>
</div>
<div class="mx-5">
<p class="lead">Rate</p>
<input type="range" min="0.1" max="10" value="1" id="rate" step="0.1" />
<span id="rate-label" class="ms-2">1</span>
</div>
<div>
<p class="lead">Pitch</p>
<input type="range" min="0" max="2" value="1" step="0.1" id="pitch" />
<span id="pitch-label" class="ms-2">1</span>
</div>

<head>
<title>Text to Speech</title>
<meta charset="UTF-8">
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" />
</head>

<body class="container mt-5 bg-dark">
<h1 class="text-light">Text to Speech</h1>
<p class="lead text-light mt-4">Select Voice</p>
<select id="voices" class="form-select bg-secondary text-light"></select>
<div class="d-flex mt-4 text-light">
<div>
<p class="lead">Volume</p>
<input type="range" min="0.1" max="1" value="1" step="0.1" id="volume" />
<span id="volume-label" class="ms-2">1</span>
</div>
<textarea class="form-control bg-dark text-light mt-5" cols="30" rows="10" placeholder="Type here..."></textarea>
<div class="mb-5">
<button id="start" class="btn btn-success mt-5 me-3">Start</button>
<button id="pause" class="btn btn-warning mt-5 me-3">Pause</button>
<button id="resume" class="btn btn-info mt-5 me-3">Resume</button>
<button id="cancel" class="btn btn-danger mt-5 me-3">Cancel</button>
<div class="mx-5">
<p class="lead">Rate</p>
<input type="range" min="0.1" max="10" value="1" id="rate" step="0.1" />
<span id="rate-label" class="ms-2">1</span>
</div>
</body>
<!-- script tag goes here -->
<script src="./textToSpeech.js"></script>
</html>
<div>
<p class="lead">Pitch</p>
<input type="range" min="0" max="2" value="1" step="0.1" id="pitch" />
<span id="pitch-label" class="ms-2">1</span>
</div>
</div>
<textarea class="form-control bg-dark text-light mt-5" cols="30" rows="10" placeholder="Type here..."></textarea>
<div class="mb-5">
<button id="start" class="btn btn-success mt-5 me-3">Start</button>
<button id="pause" class="btn btn-warning mt-5 me-3">Pause</button>
<button id="resume" class="btn btn-info mt-5 me-3">Resume</button>
<button id="cancel" class="btn btn-danger mt-5 me-3">Cancel</button>
</div>
<div class="position-absolute top-0 end-0">
<a class="btn btn-primary" href="https://github.com/zolomohan/text-to-speech/"><span>View on
GitHub </span>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path
d="M8 0a8 8 0 0 0-2.5 15.6c.4 0 .5-.2.5-.4v-1.5c-2 .4-2.5-.5-2.7-1 0-.1-.5-.9-.8-1-.3-.2-.7-.6 0-.6.6 0 1 .6 1.2.8.7 1.2 1.9 1 2.4.7 0-.5.2-.9.5-1-1.8-.3-3.7-1-3.7-4 0-.9.3-1.6.8-2.2 0-.2-.3-1 .1-2 0 0 .7-.3 2.2.7a7.4 7.4 0 0 1 4 0c1.5-1 2.2-.8 2.2-.8.5 1.1.2 2 .1 2.1.5.6.8 1.3.8 2.2 0 3-1.9 3.7-3.6 4 .3.2.5.7.5 1.4v2.2c0 .2.1.5.5.4A8 8 0 0 0 16 8a8 8 0 0 0-8-8z" />
</svg>
</a>
</div>
</body>
<!-- script tag goes here -->
<script src="./textToSpeech.js"></script>

</html>
74 changes: 66 additions & 8 deletions textToSpeech.js
Original file line number Diff line number Diff line change
@@ -1,49 +1,107 @@
// Initialize new SpeechSynthesisUtterance object
let speech = new SpeechSynthesisUtterance();

// Set speech Language
speech.lang = "en";

// global array of available voices
let voices = [];
window.speechSynthesis.onvoiceschanged = () => {
voices = window.speechSynthesis.getVoices();

speechSynthesis.onvoiceschanged = () => {
populateVoiceList();
}

function populateVoiceList() {
// Get list of voices
voices = speechSynthesis.getVoices();

// Sort the list alphabetically
voices.sort((a, b) => {
let fa = a.name.toLowerCase(),
fb = b.name.toLowerCase();
if (fa < fb) { return -1; }
if (fa > fb) { return 1; }
return 0;
});

// Initially set the First Voice in the Array.
speech.voice = voices[0];

let voiceSelect = document.querySelector("#voices");
voices.forEach((voice, i) => (voiceSelect.options[i] = new Option(voice.name, i)));
};

voices.forEach((voice, i) => {
let option = document.createElement("option");
option.textContent = voices[i].name + " (" + voices[i].lang + ")";

// Set the Index as the value, which we'll use later when the user updates the Voice using the Select Menu.
option.setAttribute("value", i);

voiceSelect.appendChild(option);
})
}

// If the Browser supports speechSynthesis populate Voice List
if (typeof speechSynthesis !== "undefined") {
populateVoiceList();
}

document.querySelector("#rate").addEventListener("input", () => {
// Get rate Value from the input
const rate = document.querySelector("#rate").value;

// Set rate property of the SpeechSynthesisUtterance instance
speech.rate = rate;

// Update the rate label
document.querySelector("#rate-label").innerHTML = rate;
});

document.querySelector("#volume").addEventListener("input", () => {
// Get volume value from the input
const volume = document.querySelector("#volume").value;

// Set volume property of the SpeechSynthesisUtterance instance
speech.volume = volume;

// Update the volume label
document.querySelector("#volume-label").innerHTML = volume;
});

document.querySelector("#pitch").addEventListener("input", () => {
// Get pitch value from the input
const pitch = document.querySelector("#pitch").value;

// Set pitch property of the SpeechSynthesisUtterance instance
speech.pitch = pitch;

// Update the pitch label
document.querySelector("#pitch-label").innerHTML = pitch;
});

document.querySelector("#voices").addEventListener("change", () => {
// On Voice change, use the value of the select menu (which is the index of the voice in the global voice array)
speech.voice = voices[document.querySelector("#voices").value];
});

document.querySelector("#start").addEventListener("click", () => {
// Set the text property with the value of the textarea
speech.text = document.querySelector("textarea").value;
window.speechSynthesis.speak(speech);

// Start Speaking
speechSynthesis.speak(speech);
});

document.querySelector("#pause").addEventListener("click", () => {
window.speechSynthesis.pause();
// Pause the speechSynthesis instance
speechSynthesis.pause();
});

document.querySelector("#resume").addEventListener("click", () => {
window.speechSynthesis.resume();
// Resume the paused speechSynthesis instance
speechSynthesis.resume();
});

document.querySelector("#cancel").addEventListener("click", () => {
window.speechSynthesis.cancel();
// Cancel the speechSynthesis instance
speechSynthesis.cancel();
});