Skip to content

Instantly share code, notes, and snippets.

@Chengings
Last active November 24, 2021 18:55
Show Gist options
  • Save Chengings/6e84c684b57129e118e3c43b8b434fc5 to your computer and use it in GitHub Desktop.
Save Chengings/6e84c684b57129e118e3c43b8b434fc5 to your computer and use it in GitHub Desktop.
Web Speech API
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width">
<title>Speech synthesiser</title>
<style>
body, html {
margin: 0;
}
html {
height: 100%;
}
body {
height: 90%;
max-width: 800px;
margin: 0 auto;
}
h1, p {
font-family: sans-serif;
text-align: center;
padding: 20px;
}
.txt, select, form > div {
display: block;
margin: 0 auto;
font-family: sans-serif;
font-size: 16px;
padding: 5px;
}
select {
width: 83%;
}
form > div {
width: 81%;
}
.txt, form > div {
margin-bottom: 10px;
font-size: 1.3rem;
overflow: auto;
}
.clearfix {
clear: both;
}
label {
float: left;
width: 10%;
line-height: 1.5;
}
.rate-value, .pitch-value {
float: right;
width: 5%;
line-height: 1.5;
}
#rate, #pitch {
float: right;
width: 81%;
}
.controls {
text-align: center;
margin-top: 10px;
}
.controls button {
padding: 15px;
}
</style>
</head>
<body>
<h1>Say it!</h1>
<form>
<textarea cols="50" rows="12" class="txt"></textarea>
<div class="controls">
<button id="play" type="submit">Say πŸ”Š</button>
<button type="button" id="read-cb">Read Clipboard & Say πŸ“‹πŸ”Š</button>
</div>
<div>
<label for="rate">Rate</label><input type="range" min="0.5" max="2" value="1" step="0.1" id="rate">
<div class="rate-value">1</div>
<div class="clearfix"></div>
</div>
<div>
<label for="pitch">Pitch</label><input type="range" min="0" max="2" value="1" step="0.1" id="pitch">
<div class="pitch-value">1</div>
<div class="clearfix"></div>
</div>
<select></select>
</form>
<script>
let synth = window.speechSynthesis;
let inputForm = document.querySelector('form')
let inputTxt = document.querySelector('.txt')
let voiceSelect = document.querySelector('select')
let pitch = document.querySelector('#pitch')
let pitchValue = document.querySelector('.pitch-value')
let rate = document.querySelector('#rate')
let rateValue = document.querySelector('.rate-value')
const readClipboard = document.querySelector('#read-cb')
let voices = [];
function populateVoiceList() {
voices = synth.getVoices().sort(function (a, b) {
const langA = a.lang, langB = b.lang;
if (langA < langB) {
return -1
} else if (langA > langB) {
return 1
}
return 0
})
let selectedIndex = voiceSelect.selectedIndex;
if (voiceSelect.selectedIndex < 0) {
let defaultNavLangIndex, customNavLangIndex;
voices.forEach(({ name, lang }, index) => {
if (lang.toLowerCase() === navigator.language.toLowerCase()) {
defaultNavLangIndex = index;
}
// For Edge only, prefer en-gb natural voice.
if (/natural.*english.*united.*kingdom/i.test(name)) {
customNavLangIndex = index;
}
})
selectedIndex = customNavLangIndex || defaultNavLangIndex || 0;
}
voiceSelect.innerHTML = '';
voices.forEach((voice, i) => {
let option = document.createElement('option')
option.textContent = `${voices[i].name} (${voices[i].lang})`;
option.setAttribute('data-lang', voices[i].lang)
option.setAttribute('data-name', voices[i].name)
voiceSelect.appendChild(option)
})
voiceSelect.selectedIndex = selectedIndex
}
populateVoiceList();
function speak(){
if (synth.speaking) {
synth.cancel()
}
if (inputTxt.value !== '') {
let utterThis = new SpeechSynthesisUtterance(inputTxt.value)
utterThis.onend = function (event) {
console.log('SpeechSynthesisUtterance.onend')
}
utterThis.onerror = function (event) {
console.error('SpeechSynthesisUtterance.onerror')
}
let selectedOption = voiceSelect.selectedOptions[0].getAttribute('data-name')
utterThis.voice = voices.find((voice, index) => voice.name === selectedOption)
utterThis.pitch = pitch.value;
utterThis.rate = rate.value;
synth.speak(utterThis)
}
}
if (speechSynthesis.onvoiceschanged !== undefined) {
speechSynthesis.onvoiceschanged = populateVoiceList;
}
inputForm.onsubmit = function(event) {
event.preventDefault()
speak()
inputTxt.blur()
}
pitch.oninput = function() {
pitchValue.textContent = pitch.value;
}
rate.oninput = function() {
rateValue.textContent = rate.value;
}
voiceSelect.onchange = function(){
speak()
}
readClipboard.onclick = e => {
navigator.clipboard.readText().then(txt => {
inputTxt.value = txt
speak()
})
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment