Skip to content

Edvins Antonovs

Build a Text-to-Speech component in React

Text-to-speech feature is now available on relatively any website or blog. It's a game changer that you can listen to the content instead of reading it. Especially effective for people with visual or cognitive impairments or on the go. I came up with the idea to implement it for my blog, so this is how I started researching this topic which ended up being a tutorial for you. So in this tutorial, we will go through the process of building a text-to-speech component in React. We will use the Web Speech API to implement the text-to-speech functionality.


What we are building

This is what the result will look like.


Text To Speech


Can I use window.speechSynthesis?

caniuse speech


Initial setup

You can use any React boilerplate to get started. I will be using the Create React App boilerplate to create a new React app.

You can install it using the following command:

1npx create-react-app text-to-speech

Once the app is created, you can start the development server using the following command:

1cd text-to-speech
2yarn start

Creating the Text-to-Speech component

The first step is to create a new component called TextToSpeech that will read the content of our blog post. Let's create a file called TextToSpeech.js.

TextToSpeech.js
1import React, { useState, useEffect } from "react";
2
3const TextToSpeech = ({ text }) => {
4 const [isPaused, setIsPaused] = useState(false);
5 const [utterance, setUtterance] = useState(null);
6
7 useEffect(() => {
8 const synth = window.speechSynthesis;
9 const u = new SpeechSynthesisUtterance(text);
10
11 setUtterance(u);
12
13 return () => {
14 synth.cancel();
15 };
16 }, [text]);
17
18 const handlePlay = () => {
19 const synth = window.speechSynthesis;
20
21 if (isPaused) {
22 synth.resume();
23 }
24
25 synth.speak(utterance);
26
27 setIsPaused(false);
28 };
29
30 const handlePause = () => {
31 const synth = window.speechSynthesis;
32
33 synth.pause();
34
35 setIsPaused(true);
36 };
37
38 const handleStop = () => {
39 const synth = window.speechSynthesis;
40
41 synth.cancel();
42
43 setIsPaused(false);
44 };
45
46 return (
47 <div>
48 <button onClick={handlePlay}>{isPaused ? "Resume" : "Play"}</button>
49 <button onClick={handlePause}>Pause</button>
50 <button onClick={handleStop}>Stop</button>
51 </div>
52 );
53};
54
55export default TextToSpeech;

This code defines a new functional component called TextToSpeech. The component takes the text prop as input and creates a new SpeechSynthesisUtterance object that contains the text to be spoken. The useEffect hook is used to initialise the utterance state and cancel any ongoing speech synthesis when the component is unmounted.

The component also defines three event handlers for playing, pausing, and stopping the speech synthesis. When the Play/Resume button is clicked, the speak method of the SpeechSynthesis interface is called to start/resume the speech synthesis. Similarly, the pause and cancel methods are called when the Pause and Stop buttons are clicked, respectively.

In case you are curious what we save into utterance state, here is the output of the SpeechSynthesisUtterance object.

1SpeechSynthesisUtterance
2text: "Text-to-speech feature is ..."
3lang: ""
4voice: null
5volume: 1
6rate: 1
7pitch: 1
8onstart: null
9onend: null
10onerror: null
11onpause: null
12onresume: null
13onmark: null
14onboundary: null
15addEventListener: ƒ addEventListener() {}
16dispatchEvent: ƒ dispatchEvent() {}
17removeEventListener: ƒ removeEventListener() {}
18<constructor>: "SpeechSynthesisUtterance"

Usage of the Text-to-Speech component

Okay, so now when we have a basic TextToSpeech component created, we can use it in our blog post.

BlogPost.js
1import TextToSpeech from './TextToSpeech';
2
3const BlogPost = () => {
4 const text =
5 "Text-to-speech feature is now available on relatively any website or blog. It's a game changer that you can listen to the content instead of reading it. Especially effective for people with visual or cognitive impairments or on the go. I came up with the idea to implement it for my blog, so this is how I started researching this topic which ended up being a tutorial for you. So in this tutorial, we will go through the process of building a text-to-speech component in React. We will use the `Web Speech API` to implement the text-to-speech functionality.";
6
7 return (
8 <div>
9 <h1>My Blog Post</h1>
10 <TextToSpeech text={text} />
11 <p>{text}</p>
12 </div>
13 );
14};
15
16export default BlogPost;

Testing the Text-to-Speech Component

Yay, we made it so far so let's test how it works.

Next steps, we can add more features to it, such as changing the speech synthesis's voice, speed, and pitch. We have a lot of options what we can do, but I we will cover the most valuable parts.


Adding voice, speed and pitch controls

To add controls for changing the voice, speed, and pitch of the speech synthesis, we can create new state variables for each of these properties in our TextToSpeech component. We can then add input elements to allow the user to adjust these properties.

TextToSpeech.js
1import React, { useState, useEffect } from "react";
2
3const TextToSpeech = ({ text }) => {
4 const [isPaused, setIsPaused] = useState(false);
5 const [utterance, setUtterance] = useState(null);
6 const [voice, setVoice] = useState(null);
7 const [pitch, setPitch] = useState(1);
8 const [rate, setRate] = useState(1);
9 const [volume, setVolume] = useState(1);
10
11 useEffect(() => {
12 const synth = window.speechSynthesis;
13 const u = new SpeechSynthesisUtterance(text);
14 const voices = synth.getVoices();
15
16 setUtterance(u);
17 setVoice(voices[0]);
18
19 return () => {
20 synth.cancel();
21 };
22 }, [text]);
23
24 const handlePlay = () => {
25 const synth = window.speechSynthesis;
26
27 if (isPaused) {
28 synth.resume();
29 } else {
30 utterance.voice = voice;
31 utterance.pitch = pitch;
32 utterance.rate = rate;
33 utterance.volume = volume;
34 synth.speak(utterance);
35 }
36
37 setIsPaused(false);
38 };
39
40 const handlePause = () => {
41 const synth = window.speechSynthesis;
42
43 synth.pause();
44
45 setIsPaused(true);
46 };
47
48 const handleStop = () => {
49 const synth = window.speechSynthesis;
50
51 synth.cancel();
52
53 setIsPaused(false);
54 };
55
56 const handleVoiceChange = (event) => {
57 const voices = window.speechSynthesis.getVoices();
58 setVoice(voices.find((v) => v.name === event.target.value));
59 };
60
61 const handlePitchChange = (event) => {
62 setPitch(parseFloat(event.target.value));
63 };
64
65 const handleRateChange = (event) => {
66 setRate(parseFloat(event.target.value));
67 };
68
69 const handleVolumeChange = (event) => {
70 setVolume(parseFloat(event.target.value));
71 };
72
73 return (
74 <div>
75 <label>
76 Voice:
77 <select value={voice?.name} onChange={handleVoiceChange}>
78 {window.speechSynthesis.getVoices().map((voice) => (
79 <option key={voice.name} value={voice.name}>
80 {voice.name}
81 </option>
82 ))}
83 </select>
84 </label>
85
86 <br />
87
88 <label>
89 Pitch:
90 <input
91 type="range"
92 min="0.5"
93 max="2"
94 step="0.1"
95 value={pitch}
96 onChange={handlePitchChange}
97 />
98 </label>
99
100 <br />
101
102 <label>
103 Speed:
104 <input
105 type="range"
106 min="0.5"
107 max="2"
108 step="0.1"
109 value={rate}
110 onChange={handleRateChange}
111 />
112 </label>
113 <br />
114 <label>
115 Volume:
116 <input
117 type="range"
118 min="0"
119 max="1"
120 step="0.1"
121 value={volume}
122 onChange={handleVolumeChange}
123 />
124 </label>
125
126 <br />
127
128 <button onClick={handlePlay}>{isPaused ? "Resume" : "Play"}</button>
129 <button onClick={handlePause}>Pause</button>
130 <button onClick={handleStop}>Stop</button>
131 </div>
132 );
133};
134
135export default TextToSpeech;

Whoa, that's a lot of changes to our initial code. So let's discuss what happened here. We've added new state variables for voice, pitch, rate and volume and input elements for controls. We've also added a select element to allow the user to choose from the available voices.

We've updated the handlePlay function to set the voice, rate, and pitch properties of the SpeechSynthesisUtterance object based on the current state of our component.


Now when I learnt a lot about Web Speech API, I can't wait to implement it in this blog. I hope you enjoyed this article and learned something new too. If you have any questions or suggestions, drop a comment below.

© 2024 by Edvins Antonovs. All rights reserved.