-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.html
265 lines (214 loc) · 8.16 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Text-to-Speech App with Typing Animation</title>
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="bg-gray-100 flex justify-center items-center min-h-screen p-4">
<div class="bg-white rounded-lg shadow-lg p-6 w-full max-w-md">
<h1 class="text-2xl font-bold mb-4 text-center text-gray-800">
Text-to-Speech App with Typing Animation
</h1>
<textarea id="textInput" class="w-full p-2 mb-4 border rounded resize-y" rows="4" placeholder="Enter text to speak">
Text-to-Speech App with Typing Animation
A simple interactive web application that converts text to speech with a typing animation effect.
🌟 Features
Multi-language Support: Choose from a variety of languages for text-to-speech conversion.
Voice Selection: Pick from multiple voices available for each language.
Adjustable Speech Rate: Customize the speed of speech output.
Real-time Typing Animation: Watch as the spoken text appears on screen with a typewriter-like effect.
Responsive Design: Beautifully crafted UI that works seamlessly on both desktop and mobile devices.
🚀 Quick Start
Open the index.html file in a modern web browser.
Enter the text you want to convert to speech in the textarea.
Select your preferred language and voice.
Adjust the speech rate if desired.
Click the "Speak" button to start the text-to-speech conversion with typing animation.
🛠️ Technologies Used
HTML5
CSS3 (with Tailwind CSS for styling)
JavaScript (ES6+)
Web Speech API
🎨 User Interface
The app features a clean, intuitive interface with the following components:
Text input area
Language selection dropdown
Voice selection dropdown
Speech rate slider
Speak and Cancel buttons
Output display area for the typing animation
🔧 Key Components
Language and Voice Selection:
Dynamically populates available languages and voices.
Updates voice options based on the selected language.
Speech Synthesis:
Utilizes the Web Speech API for text-to-speech conversion.
Supports cancellation of ongoing speech.
Typing Animation:
Implements a word-by-word typing effect synchronized with speech.
Uses Intl.Segmenter for accurate word segmentation across languages.
Responsive Design:
Employs Tailwind CSS for a mobile-friendly layout.
🌐 Browser Compatibility
This app works best on modern browsers that support the Web Speech API and ES6+ features. For optimal performance, use the latest versions of Chrome, Firefox, Safari, or Edge.
</textarea>
<div class="mb-4">
<label for="languageSelect" class="block mb-2 text-sm font-medium text-gray-700">Select Language:
</label>
<select id="languageSelect" class="w-full p-2 border rounded">
<option value="">Select a language</option>
</select>
</div>
<div class="mb-4">
<label for="voiceSelect" class="block mb-2 text-sm font-medium text-gray-700">Select Voice:</label>
<select id="voiceSelect" class="w-full p-2 border rounded">
<option value="">Select a language first</option>
</select>
</div>
<div class="mb-4">
<label for="rateRange" class="block mb-2 text-sm font-medium text-gray-700">Speech Rate: <span
id="rateValue">1</span></label>
<input type="range" id="rateRange" min="0.5" max="2" value="1" step="0.1" class="w-full" />
</div>
<div class="flex justify-between mb-4">
<button id="speakButton" class="bg-green-500 text-white px-4 py-2 rounded hover:bg-green-600 transition">
Speak
</button>
<button id="cancelButton" class="bg-red-500 text-white px-4 py-2 rounded hover:bg-red-600 transition">
Cancel
</button>
</div>
<div id="output" class="mt-4 p-2 bg-gray-100 rounded min-h-[50px] text-left whitespace-pre-wrap"></div>
</div>
<script>
const textInput = document.getElementById("textInput");
const languageSelect = document.getElementById("languageSelect");
const voiceSelect = document.getElementById("voiceSelect");
const speakButton = document.getElementById("speakButton");
const cancelButton = document.getElementById("cancelButton");
const output = document.getElementById("output");
const rateRange = document.getElementById("rateRange");
const rateValue = document.getElementById("rateValue");
let utterance = null;
let isSpeaking = false;
// Populate language list
function populateLanguageList() {
const voices = speechSynthesis.getVoices();
const languages = [
...new Set(voices.map((voice) => voice.lang)),
].sort();
languageSelect.innerHTML =
'<option value="">Select a language</option>' +
languages
.map(
(lang) =>
`<option value="${lang}"${lang === "en" ? " selected" : ""
}>${new Intl.DisplayNames(["en"], {
type: "language",
}).of(lang)} (${lang})</option>`
)
.join("");
}
// Populate voice list based on selected language
function populateVoiceList(lang) {
const voices = speechSynthesis
.getVoices()
.filter((voice) => voice.lang === lang);
voiceSelect.innerHTML = voices
.map(
(voice) =>
`<option value="${voice.name}">${voice.name}</option>`
)
.join("");
}
speechSynthesis.onvoiceschanged = populateLanguageList;
// Update voice list when language is changed
languageSelect.addEventListener("change", (event) => {
populateVoiceList(event.target.value);
});
// Update rate value display
rateRange.addEventListener("input", () => {
rateValue.textContent = rateRange.value;
});
speakButton.addEventListener("click", () => {
const text = textInput.value;
if (text && !isSpeaking && languageSelect.value) {
speakText(text);
}
});
cancelButton.addEventListener("click", () => {
if (isSpeaking) {
speechSynthesis.cancel();
isSpeaking = false;
}
});
function speakText(text) {
if (utterance) {
speechSynthesis.cancel();
}
utterance = new SpeechSynthesisUtterance(text);
output.textContent = "";
isSpeaking = true;
// Set selected language and voice
utterance.lang = languageSelect.value;
const voices = speechSynthesis.getVoices();
const selectedVoice = voices.find(
(voice) => voice.name === voiceSelect.value
);
if (selectedVoice) utterance.voice = selectedVoice;
// Set speech rate
utterance.rate = parseFloat(rateRange.value);
// Use Intl.Segmenter for word segmentation
const segmenter = new Intl.Segmenter(utterance.lang, {
granularity: "word",
});
const segments = segmenter.segment(text);
const words = Array.from(segments).map(
(segment) => segment.segment
);
let wordIndex = 0;
let previousWordEnd = 0;
utterance.onboundary = (event) => {
console.log(event);
if (event.name === "word") {
console.log(words[wordIndex]);
const currentWordStarting = event.charIndex;
let word = text.slice(
previousWordEnd,
currentWordStarting
);
const currentWordEnd =
event.charIndex + event.charLength;
previousWordEnd = currentWordEnd;
word += event.target.text.slice(
event.charIndex,
event.charIndex + event.charLength
);
animateText(word);
wordIndex++;
}
};
utterance.onend = () => {
isSpeaking = false;
};
speechSynthesis.speak(utterance);
}
function animateText(text) {
const delay = 10; // Delay between each character (in milliseconds)
let index = 0;
const typingInterval = setInterval(() => {
output.textContent += text[index];
index++;
if (index === text.length) {
clearInterval(typingInterval);
}
}, delay);
}
// Initial population of language list
populateLanguageList();
setTimeout(populateLanguageList, 10);
</script>
</body>
</html>