How to Add Text-to-Speech with Word Highlighting to Blogger (Free HTML, CSS and JavaScript Code)
Adding a text-to-speech feature to your Blogger website can make your articles more accessible and convenient for readers. Instead of reading an entire article, visitors can simply tap the Play button and listen to your content.
In this tutorial, you will learn how to add a free text-to-speech player to Blogger with Play, Pause, Resume, adjustable reading speed and real-time word highlighting. The player uses HTML, CSS and JavaScript, so you don't need to upload MP3 files or pay for an audio hosting service.
What Is Text-to-Speech in Blogger?
Text-to-speech (TTS) is a technology that converts written text into spoken audio. With a TTS player on your Blogger website, visitors can listen to your articles directly in their browsers.
The browser's built-in Speech Synthesis API can generate speech without requiring a separate audio file or a paid text-to-speech service.
A text-to-speech player with word highlighting can offer the following features:
Play: Start reading the blog post aloud.
Pause: Temporarily stop the narration.
Resume: Continue reading after pausing.
Word highlighting: Highlight the word currently being spoken.
Reading speed: Allow readers to adjust the narration speed.
Automatic scrolling: Keep the highlighted word visible as the article is read.
Mobile compatibility: Provide a responsive player for mobile and desktop visitors.
Why Add a Text-to-Speech Player to Your Blog?
A text-to-speech player can make your Blogger website easier to use, particularly for readers who prefer listening to reading.
1. Improve accessibility
Some visitors may have difficulty reading long articles on a screen. Audio narration provides another way to access your content.
2. Make articles more convenient
Readers can listen to articles while relaxing or doing other activities, provided they keep the page open.
3. Highlight words while speaking
Word highlighting helps readers follow the narration visually. This can be especially useful for language learners and people who prefer reading along with audio.
4. No need to upload audio files
The browser generates speech from your article's text. You don't need to record your own voice or upload an MP3 for every blog post.
5. Give readers control
Visitors can pause the narration or change the playback speed according to their preferences.
How Does Word-by-Word Highlighting Work?
The word-highlighting feature uses the browser's speech-boundary events to identify the position of the word being spoken.
Step 1: Start narration
When a visitor taps Play, JavaScript extracts the article text and starts the browser's speech engine.
Word
Step 2: Highlight the spoken word
When the browser reports a word boundary, JavaScript highlights the corresponding word in yellow.
Step 3: Automatic scrolling
When the highlighted word moves outside the visible area, the page scrolls to bring it into view.
Complete HTML, CSS and JavaScript Code for Blogger
The following code combines the audio player and word-highlighting feature in one script. It is designed for Blogger posts and uses the browser's built-in speech synthesis.
<style>
/* Text-to-speech player */
.tts-player {
display: flex;
align-items: center;
gap: 12px;
padding: 14px 18px;
margin: 20px 0;
background: #f3f6fa;
border: 1px solid #dce3ed;
border-radius: 12px;
font-family: Arial, sans-serif;
box-sizing: border-box;
max-width: 100%;
}
.tts-player button {
border: none;
border-radius: 50%;
width: 42px;
height: 42px;
flex-shrink: 0;
cursor: pointer;
font-size: 18px;
color: white;
background: #1769e0;
}
.tts-player button:disabled {
opacity: .4;
cursor: not-allowed;
}
.tts-info {
flex: 1;
min-width: 0;
}
.tts-title {
font-weight: bold;
font-size: 14px;
color: #172337;
}
.tts-status {
font-size: 12px;
color: #64748b;
margin-top: 4px;
}
.tts-player select {
max-width: 115px;
padding: 6px;
border: 1px solid #cbd5e1;
border-radius: 6px;
background: white;
font-size: 13px;
}
/* Highlight spoken word */
.tts-word-active {
background: #ffeb3b !important;
color: #111 !important;
border-radius: 3px;
box-shadow: 0 0 0 2px #ffeb3b;
}
/* Mobile responsive design */
@media (max-width: 480px) {
.tts-player {
gap: 8px;
padding: 10px;
}
.tts-player button {
width: 36px;
height: 36px;
font-size: 15px;
}
.tts-player select {
max-width: 82px;
font-size: 12px;
}
}
</style>
<!-- AUDIO PLAYER -->
<div class="tts-player" id="blogTTS">
<button id="ttsPlay" type="button"
aria-label="Play article">▶</button>
<button id="ttsPause" type="button"
aria-label="Pause article" disabled>Ⅱ</button>
<div class="tts-info">
<div class="tts-title">Listen to this article</div>
<div class="tts-status" id="ttsStatus" aria-live="polite">
Ready to play
</div>
</div>
<select id="ttsSpeed" aria-label="Reading speed">
<option value="0.8">0.8x</option>
<option value="1" selected>1x speed</option>
<option value="1.2">1.2x</option>
<option value="1.5">1.5x</option>
<option value="2">2x</option>
</select>
</div>
<script>
(function () {
"use strict";
const play = document.getElementById("ttsPlay");
const pause = document.getElementById("ttsPause");
const status = document.getElementById("ttsStatus");
const speed = document.getElementById("ttsSpeed");
if (!play || !pause || !status || !speed) return;
if (!("speechSynthesis" in window) ||
!("SpeechSynthesisUtterance" in window)) {
status.textContent = "Audio is not supported.";
play.disabled = true;
return;
}
let article = null;
let words = [];
let utterance = null;
let paused = false;
let generation = 0;
let lastWord = -1;
let spokenWords = [];
// Locate the Blogger article.
function getArticle() {
return document.querySelector(
".post-body, .post-content, .entry-content"
);
}
// Wrap each article word in a span.
function prepareArticle() {
article = getArticle();
if (!article) return false;
// Remove old word wrappers.
article.querySelectorAll(".tts-word").forEach(span => {
span.replaceWith(document.createTextNode(span.textContent));
});
words = [];
const walker = document.createTreeWalker(
article,
NodeFilter.SHOW_TEXT,
{
acceptNode(node) {
if (!node.nodeValue.trim()) {
return NodeFilter.FILTER_REJECT;
}
if (node.parentElement.closest(
"script,style,button,select,iframe," +
"noscript,.tts-player,svg"
)) {
return NodeFilter.FILTER_REJECT;
}
return NodeFilter.FILTER_ACCEPT;
}
}
);
const nodes = [];
while (walker.nextNode()) {
nodes.push(walker.currentNode);
}
nodes.forEach(node => {
const text = node.nodeValue;
const fragment = document.createDocumentFragment();
const regex = /\S+/g;
let last = 0;
let match;
while ((match = regex.exec(text)) !== null) {
if (match.index > last) {
fragment.appendChild(
document.createTextNode(
text.slice(last, match.index)
)
);
}
const span = document.createElement("span");
span.className = "tts-word";
span.textContent = match[0];
fragment.appendChild(span);
words.push(span);
last = regex.lastIndex;
}
if (last < text.length) {
fragment.appendChild(
document.createTextNode(text.slice(last))
);
}
node.parentNode.replaceChild(fragment, node);
});
return words.length > 0;
}
// Clear previous highlight.
function clearHighlight() {
if (!article) return;
article.querySelectorAll(".tts-word-active")
.forEach(el => {
el.classList.remove("tts-word-active");
});
lastWord = -1;
}
// Highlight the current spoken word.
function highlightWord(index) {
if (index < 0 || index >= words.length) return;
if (lastWord === index) return;
if (lastWord >= 0 && words[lastWord]) {
words[lastWord].classList.remove("tts-word-active");
}
lastWord = index;
const el = words[index];
el.classList.add("tts-word-active");
const rect = el.getBoundingClientRect();
if (rect.top < 0 || rect.bottom > window.innerHeight) {
el.scrollIntoView({
behavior: "smooth",
block: "center"
});
}
}
// Find the word using its character position.
function findWordIndex(charIndex) {
let low = 0;
let high = spokenWords.length - 1;
while (low <= high) {
const mid = (low + high) >> 1;
const word = spokenWords[mid];
if (charIndex < word.start) {
high = mid - 1;
} else if (charIndex >= word.end) {
low = mid + 1;
} else {
return mid;
}
}
return -1;
}
// Play or resume the article.
function startReading() {
if (paused && speechSynthesis.paused) {
speechSynthesis.resume();
paused = false;
status.textContent = "Playing";
pause.disabled = false;
return;
}
speechSynthesis.cancel();
generation++;
const currentGeneration = generation;
clearHighlight();
if (!prepareArticle()) {
status.textContent = "Article text not found.";
return;
}
const text = article.innerText.trim();
if (!text) {
status.textContent = "No article text found.";
return;
}
spokenWords = [];
const regex = /\S+/g;
let match;
while ((match = regex.exec(text)) !== null) {
spokenWords.push({
start: match.index,
end: regex.lastIndex
});
}
utterance = new SpeechSynthesisUtterance(text);
utterance.rate = Number(speed.value);
utterance.lang =
article.closest("[lang]")?.getAttribute("lang") ||
document.documentElement.lang ||
"en-US";
utterance.onstart = function () {
if (currentGeneration !== generation) return;
status.textContent = "Playing article...";
pause.disabled = false;
};
utterance.onboundary = function (event) {
if (currentGeneration !== generation) return;
if (event.name && event.name !== "word") return;
const index = findWordIndex(event.charIndex);
if (index !== -1) {
highlightWord(index);
}
};
utterance.onend = function () {
if (currentGeneration !== generation) return;
status.textContent = "Finished reading";
pause.disabled = true;
paused = false;
};
utterance.onerror = function (event) {
if (currentGeneration !== generation) return;
if (event.error !== "canceled" &&
event.error !== "interrupted") {
status.textContent = "Unable to play audio.";
}
pause.disabled = true;
paused = false;
};
paused = false;
status.textContent = "Starting...";
speechSynthesis.speak(utterance);
}
play.addEventListener("click", startReading);
pause.addEventListener("click", function () {
if (speechSynthesis.speaking &&
!speechSynthesis.paused) {
speechSynthesis.pause();
paused = true;
status.textContent = "Paused";
pause.disabled = true;
}
});
speed.addEventListener("change", function () {
if (speechSynthesis.speaking &&
!speechSynthesis.paused) {
startReading();
}
});
window.addEventListener("pagehide", function () {
generation++;
speechSynthesis.cancel();
});
})();
</script>How to Add This Code to Blogger
Follow these steps to add the text-to-speech player to your website.
Sign in to your
Blogger dashboard.Select the blog where you want to add the audio player.
Open Layout from the left sidebar.
Click Add a Gadget in the section where you want the player to appear.
Select HTML/JavaScript.
Paste the complete code above into the content box.
Click Save and then save the Blogger layout.
You can also insert the code into an individual post by switching to the post's HTML view. If you use it in individual posts, avoid adding multiple copies of the same player on a page.
How to Test the Text-to-Speech Player
After installing the code, open a published blog post and test the following features.
Feature | How to test |
|---|---|
Play | Tap the Play button and listen to the article. |
Pause | Tap Pause while the narration is playing. |
Resume | Tap Play again after pausing. |
Speed | Select a different reading speed. |
Highlighting | Check whether the spoken word is highlighted in yellow. |
Auto-scroll | Allow the narration to continue until the page scrolls. |
Expected result
Listen to this article
Playing article...
Text-to-speech makes it easier to follow an article while listening.
Illustration of the expected player and highlighting effect.
Common Problems and Solutions
1. The Play button does not work
Make sure you are using a modern browser that supports the Web Speech API. Some browsers also require the visitor to tap Play before speech can begin.
2. Word highlighting does not work
Word highlighting depends on speech-boundary events. Some browser and voice combinations don't provide reliable word positions. Test the feature in another supported browser.
3. The player cannot find the article
The code looks for Blogger's common article-content classes: .post-body, .post-content and .entry-content. If your theme uses a different class, update the getArticle() function to match it.
4. The narration uses the wrong language
The code uses the language declared in the article or the page. You can change the utterance.lang setting to a supported language code, such as en-US for American English or hi-IN for Hindi. The actual voice depends on the visitor's device.
5. The narration restarts when changing speed
The current implementation restarts the article when the reading speed changes during playback. It does not continue from the exact word where it was interrupted.
Frequently Asked Questions
Is this Blogger text-to-speech code free?
Yes. The code uses the browser's built-in speech synthesis, so there is no subscription fee or requirement to upload audio files.
Does the code work on mobile devices?
It is designed to work on mobile and desktop browsers that support speech synthesis. Word highlighting may vary depending on the browser and voice.
Can I use this code on every Blogger post?
Yes. You can add the player to individual posts or place it in a suitable theme location. If you add it globally, make sure the article selector identifies the correct post content.
Does this feature improve SEO?
Adding a text-to-speech player can improve accessibility and provide an additional way to consume your content. However, it does not automatically improve search rankings. Continue to focus on original content, page speed, mobile usability and relevant SEO practices.
Can visitors download the audio?
No. This code plays speech directly in the browser. It does not generate or save an MP3 file.
Conclusion
Adding a text-to-speech player with word highlighting to Blogger is a practical way to give visitors another way to consume your articles. With a simple combination of HTML, CSS and JavaScript, you can offer narration, playback controls, reading-speed options and visual highlighting without paying for audio hosting.
The main limitation is that word highlighting depends on the speech engine's ability to report word boundaries. Test the player on the browsers and devices commonly used by your readers before publishing it across your website.
0 Comments