Skip to content

Instantly share code, notes, and snippets.

@prof3ssorSt3v3
Last active January 14, 2024 08:13
Show Gist options
  • Save prof3ssorSt3v3/1c275f7e1d534945aefba5104077bce0 to your computer and use it in GitHub Desktop.
Save prof3ssorSt3v3/1c275f7e1d534945aefba5104077bce0 to your computer and use it in GitHub Desktop.
Code from video on Intro to Web Text to Speech
let VOICE = null;
let synth = window.speechSynthesis;
let voices = synth.getVoices();
(function addListeners() {
document.getElementById('voiceSelect').addEventListener('change', changeVoice);
document.getElementById('btnRead').addEventListener('click', readParas);
document.getElementById('btnPause').addEventListener('click', () => {
synth.pause();
});
document.getElementById('btnResume').addEventListener('click', () => {
synth.resume();
});
document.getElementById('rate').addEventListener('input', (ev) => {
document.getElementById('rate-value').textContent = ev.target.value;
});
document.getElementById('pitch').addEventListener('input', (ev) => {
document.getElementById('pitch-value').textContent = ev.target.value;
});
document.getElementById('volume').addEventListener('input', (ev) => {
document.getElementById('volume-value').textContent = ev.target.value;
});
//build the select list
setTimeout(() => {
if (voices.length === 0) {
voices = synth.getVoices();
}
loadVoices();
}, 100);
})();
function loadVoices() {
//build the select list
// voice .lang 'en-CA', name: 'Karen', default: true|false
for (let i = 0; i < voices.length; i++) {
if (!voices[i].lang.startsWith('en-')) continue;
const option = document.createElement('option');
option.textContent = voices[i].name + ' (' + voices[i].lang + ')';
if (voices[i].default) {
option.className = 'picked';
VOICE = voices[i];
}
option.setAttribute('data-lang', voices[i].lang);
option.setAttribute('data-name', voices[i].name);
document.getElementById('voiceSelect').appendChild(option);
}
}
function readParas(ev) {
//read the text from the inputs and speak it
const inputs = document.querySelectorAll('.sentences input');
inputs.forEach((input, idx) => {
const txt = input.value;
const utter = new SpeechSynthesisUtterance(txt);
utter.voice = VOICE;
//change the rate, pitch and volume
utter.rate = document.getElementById('rate').value;
utter.pitch = document.getElementById('pitch').value;
utter.volume = document.getElementById('volume').value;
synth.speak(utter);
});
}
function changeVoice(ev) {
//pick a voice from the list
const select = ev.target;
const index = select.selectedIndex;
const name = select.options[index].getAttribute('data-name');
select.querySelector('.picked').removeAttribute('class');
for (let i = 0; i < voices.length; i++) {
if (voices[i].name === name) {
VOICE = voices[i];
select.options[index].className = 'picked';
}
}
console.log(VOICE);
}
html {
color-scheme: dark light;
font-size: 20px;
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
}
.sentences {
display: flex;
flex-direction: column;
}
input[type='text'] {
font-size: 1.2rem;
padding: 0.2rem 1rem;
margin: 1rem;
width: 50ch;
}
.buttons {
display: flex;
flex-direction: row;
}
button {
background-color: steelblue;
color: white;
font-size: 1.2rem;
padding: 0.2rem 2rem;
margin-inline: 2rem;
border: none;
cursor: pointer;
}
.controls {
display: flex;
flex-direction: row;
gap: 2rem;
}
.controls p {
display: flex;
flex-direction: column;
}
.controls p * {
font-size: 1.2rem;
}
option.picked {
text-align: right;
font-weight: bold;
}
/* deprecated in most places from CSS 2.1
@media aural {
body {
voice-family: Karen, female;
pause-before: 100ms;
cue-after: url('./effect.mp3');
stress: 20;
richness: 90;
speech-rate: medium;
azimuth: 0deg;
elevation: 30deg;
volume: 100;
}
}
*/
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>SpeechSynthesis - Text 2 Speech</title>
<link href="./main.css" rel="stylesheet" />
<script src="./app.js" type="module"></script>
</head>
<body>
<header>
<h1>SpeechSynthesis &amp; SpeechSynthesisUtterance</h1>
</header>
<div class="sentences">
<input type="text" value="So, I asked my girlfriend Matilda if she wanted to waltz." />
<input type="text" value="Pineapple Apple Pen." />
</div>
<div class="buttons">
<button id="btnRead">READ</button>
<button id="btnPause">PAUSE playback</button>
<button id="btnResume">RESUME playback</button>
</div>
<div class="controls">
<p>
<label for="voiceSelect">Voice List</label>
<select id="voiceSelect" name="voiceSelect"></select>
</p>
<p>
<label for="pitch">Pitch (0 - 2)</label>
<input type="range" id="pitch" min="0" max="2" value="1" step="0.1" />
<span id="pitch-value">1</span>
</p>
<p>
<label for="rate">Rate (1 - 10)</label>
<input type="range" id="rate" min="1" max="10" value="1" step="1" />
<span id="rate-value">1</span>
</p>
<p>
<label for="volume">Volume (0 - 1)</label>
<input type="range" id="volume" min="0" max="1" value="0.5" step="0.1" />
<span id="volume-value">0.5</span>
</p>
</div>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment