Skip to content

Instantly share code, notes, and snippets.

@blackmjck
Last active April 5, 2017 14:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save blackmjck/cb173701dd9b9ddb104a to your computer and use it in GitHub Desktop.
Save blackmjck/cb173701dd9b9ddb104a to your computer and use it in GitHub Desktop.
Playing with the Web Speech API
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Speech Synthesis in JavaScript</title>
<style>
label {
display: block;
margin-bottom: 0.5em;
}
label * {
display: block;
}
label select[multiple] {
height: 11.75em;
}
</style>
</head>
<body>
<section class="container">
<h1>Speech Synthesis API</h1>
<form id="test-zone"></form>
<small>(*Only works in Chrome 33+, Edge, Safari 7+, and Firefox 49+)</small>
</section>
<script src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.16/d3.min.js"></script>
<script>
/**
* Playing with the Web Speech API synthesis module
* https://developer.mozilla.org/en-US/docs/Web/API/Web_Speech_API
*
* (Using D3 as the DOM manipulator as I'm already playing with it elsewhere)
*/
var synth = speechSynthesis || window.speechSynthesis,
utterance = SpeechSynthesisUtterance || window.SpeechSynthesisUtterance;
// in Chrome, the voices are loaded asynchronously and will trigger 'onvoiceschanged' once loaded.
if( !getVoices().length ) {
synth.onvoiceschanged = function() {
synth.onvoiceschanged = null;
setStage( getVoices() );
};
// in Firefox, they're loaded at page load time, so no need for async calls
} else {
setStage( getVoices() );
}
function getVoices() {
return synth.getVoices();
}
function setStage( voices ) {
var parent = d3.select( '#test-zone' ),
input,
selector,
pitch,
rate,
trigger,
pause,
resume,
killSwitch,
reset;
input = parent.append( 'label' )
.text( 'Text to speak:' )
.append( 'textarea' )
.attr( {
id: 'input',
placeholder: 'Enter text here...'
} )
.style( {
'min-width': '30%',
'min-height': '5em'
} );
selector = parent.append( 'label' )
.text( 'Voice:' )
.append( 'select' )
.attr( {
id: 'voices',
multiple: 'multiple'
} );
pitch = parent.append( 'label' )
.text( 'Pitch:' )
.append( 'input' )
.attr( {
class: 'range',
id: 'pitch',
max: 2,
min: 0,
step: 0.5,
type: 'range',
value: 1
} );
rate = parent.append( 'label' )
.text( 'Rate:' )
.append( 'input' )
.attr( {
class: 'range',
id: 'rate',
max: 2,
min: 0.5,
step: 0.5,
type: 'range',
value: 1
} );
trigger = parent.append( 'button' )
.attr( {
id: 'trigger',
type: 'button'
} )
.text( 'Speak!' );
pause = parent.append( 'button' )
.attr( {
id: 'pause',
type: 'button'
} )
.text( 'Pause' );
resume = parent.append( 'button' )
.attr( {
id: 'resume',
type: 'button'
} )
.text( 'Resume' );
killSwitch = parent.append( 'button' )
.attr( {
id: 'kill',
type: 'button'
} )
.text( 'Cancel' );
reset = parent.append( 'button' )
.attr( {
id: 'reset',
type: 'reset'
} )
.text( 'Reset' );
selector.selectAll( 'option' )
.data( voices )
.enter()
.append( 'option' )
.attr( 'value', function( d, i ) { return i; } )
.text( function( d ) { return d.name + ' (' + d.lang + ')'; } );
trigger.on( 'click', function() {
var text = input.node().value.trim(),
vIndex = selector.node().value || 0,
voice = voices[vIndex],
r = rate.node().value || 1,
p = pitch.node().value || 1;
console.log( 'New speech time!', text, voice );
if( !input.node().value.length ) {
synth.speak( new utterance( 'You have to enter some text first, idiot' ) );
} else {
speakWithPauses( text, voice, p, r );
}
} );
pause.on( 'click', function() {
synth.paused ? console.error( 'Cannot pause while paused.' ) : synth.pause();
} );
resume.on( 'click', function() {
synth.paused ? synth.resume() : console.error( 'Cannot resume if not paused first.' );
} );
killSwitch.on( 'click', function() {
synth.cancel();
} );
}
function speakWithPauses( text, voice, pitch, rate ) {
var speeches, speech;
speeches = text.split( /\b(:?\s{2,})/i );
speeches.forEach( function( snippet ) {
speech = new utterance( snippet );
speech.voice = voice;
speech.pitch = pitch;
speech.rate = rate;
synth.speak( speech );
} );
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment