How to Add Text-to-Speech and Accessibility Features to a Website (Free, Without Third-Party APIs)
Hello, everyone! In this post, we are once again sharing a tutorial on website development and Blogger. We will cover how to add a highly useful accessibility feature, particularly for website visitors with visual impairments.
![]() |
| Speech Synthesis di Blogger |
Interestingly, we will create this feature by simply leveraging technology that is already available in your visitors' browsers.
Why Use Built-in Browser Features?
Most modern browsers—especially Chromium-based ones like Google Chrome or Microsoft Edge—already support this feature. We simply use JavaScript to call the `SpeechSynthesis` function, a text-to-speech capability built directly into the browser.
This means there is no need to download extra audio files or install third-party resources. Most importantly, you do not need to subscribe to paid APIs—or resort to the sometimes illegal use of leaked sources.
Everything is 100% free and legal.
It is also very practical to use: whenever a visitor hovers their cursor over a link or menu item, the browser automatically "speaks" the text associated with that element.
Is This Feature Only for Bloggers?
Certainly not! This script is highly flexible. Although this tutorial focuses on Blogger, you can still implement it on other platforms like WordPress or custom HTML websites. As long as the platform allows you to insert HTML and JavaScript elements, this feature will work seamlessly.
Script Installation Method
Installation Guide for Blogger Users
Installing it for bloggers is very easy:
- Log in to your Blogger dashboard
- Open the Layout menu
- Click Add a Gadget and select HTML/JavaScript
- Paste the script code we have provided below into the available field
Save the settings, then open your blog to test them immediately.
Installation Instructions for WordPress Users or Personal Websites
If you are using WordPress (you will likely need to edit `functions.php`) or building a custom website, simply place this script between the `<body>` and `</body>` tags—or, for safety, just before the closing `</body>` tag in your website's HTML structure.
Once the website has finished loading, visitors simply need to hover their cursor over the target menu for the first time. Following that initial interaction, the browser will immediately load and activate the sound.
Does This Script Make the Website Slow?
The answer is: Not really. While adding any script inevitably has some impact, this method relies heavily on the user's Chromium browser engine (rather than loading audio files from an external server), so it is very lightweight and barely affects your website's loading speed. That said, it does work in other browsers, though the audio sounds robotic; it performs best in Chromium.
Two Mode Options: Automatic vs. Manual, plus Accessibility Button
In this tutorial, we are sharing two script modes that you can choose from based on your website's needs:
- Automatic Mode (No Button): The text-to-speech feature activates immediately and is ready to read the text as soon as visitors enter your website
- Manual Mode (On/Off Button): The audio feature will only activate if the visitor manually presses the toggle switch
Our Recommendations:
It is highly recommended to use Manual Mode (On/Off button). By using a button, this feature will not disturb general visitors who do not require it; your website will continue to function as usual without any unexpected audio playback.
For those who do need voice guidance, they can simply activate the button—which you can neatly place within a dedicated Accessibility menu.
Script Text-to-Speech Otomatis
This is a Text to Speech script that will automatically turn on after the user
<script>
function RcnSuaraAksesibilitas(teks) {
if ('speechSynthesis' in window) {
window.speechSynthesis.cancel();
var speech = new SpeechSynthesisUtterance(teks);
speech.lang = 'id-ID';
speech.rate = 1.1;
speech.pitch = 1;
window.speechSynthesis.speak(speech);
}
}
document.addEventListener("DOMContentLoaded", function() {
var elemenMenu = document.querySelectorAll("a, button, input[type='button'], input[type='submit']");
elemenMenu.forEach(function(elemen) {
elemen.addEventListener("mouseenter", function() {
var teksMenu = elemen.innerText || elemen.textContent || elemen.getAttribute("aria-label") || elemen.placeholder;
if (teksMenu && teksMenu.trim() !== "") {
RcnSuaraAksesibilitas(teksMenu.trim());
}
});
elemen.addEventListener("mouseleave", function() {
if ('speechSynthesis' in window) {
window.speechSynthesis.cancel();
}
});
});
});
</script>
How the Code Works:
- The script automatically scans all navigation links, buttons, and search bars on your blog, without you having to edit the menu code one by one
- As soon as the cursor is hovered over a menu (for example, the "Home", "Profile", or "Contact" menu), the browser immediately reads it aloud in Indonesian
- If the cursor is quickly moved to another menu, the sound from the previous menu is immediately cut off to prevent the sounds from overlapping and becoming noisy
Important Browser Policy Note:
In accordance with modern browser privacy policies (Chrome, Safari, Edge), audio will not play if a user opens the site and immediately moves their cursor. Visitors must click at least once anywhere on your blog to grant "interaction permission"; once this is done, the menu reader feature will be fully active while they browse your site.
Text-to-Speech Script with Activation Button and Other Accessibility Features
This is a script that includes several accessibility settings; it’s the same one I use on this site to replace the text-resizing function that used to appear above posts—which I found rather annoying :) Here it is.
<!-- Post Body Entry Content-->
<span align='center' class='textresize' style='font-weight: bold; background-color: yellow; border:3px solid red'>
Ubah Ukuran Font :<a href='javascript:changeFontSize(1)'> [ Tambahkan + ]</a> atau <a href='javascript:changeFontSize(-1)'> [ Kurangkan - ]</a>
</span>
I removed that from the HTML editor and replaced it with this accessibility script. For a cleaner setup, you could go to your Blogger dashboard—specifically **Theme > Edit HTML**—and place the script just before the `</body>` tag; however, for a more beginner-friendly approach, you can simply add it via the **Layout** section (using the "Add a Gadget" option) as previously instructed.
Feel free to use this for free. Here is roughly what it will look like.
![]() |
| Contoh Penerapan Script dengan Tombol |
<button id="accBtn" onclick="RcntoggleAccMenu()" aria-label="Menu Aksesibilitas" style="position:fixed; bottom:120px; right:20px; z-index:9999; background:#0056b3; color:#fff; border:none; border-radius:50%; width:50px; height:50px; font-size:24px; cursor:pointer; box-shadow:0 4px 6px rgba(0,0,0,0.2);">♿</button>
<div id="accMenu" style="display:none; position:fixed; bottom:80px; right:20px; z-index:9999; background:#fff; color:#333; border:1px solid #ccc; border-radius:8px; padding:15px; width:220px; box-shadow:0 4px 10px rgba(0,0,0,0.3); font-family:sans-serif;">
<h4 style="margin:0 0 10px 0; text-align:center; border-bottom:1px solid #eee; padding-bottom:5px;">Aksesibilitas</h4>
<div style="margin-bottom:12px;">
<p style="margin:0 0 5px 0; font-size:14px;">Text Size:</p>
<button onclick="changeFontSize('increase')" style="width:45%; padding:5px; margin-right:5px; cursor:pointer;">A+</button>
<button onclick="changeFontSize('reset')" style="width:45%; padding:5px; cursor:pointer;">Reset</button>
</div>
<div style="margin-bottom:12px;">
<p style="margin:0 0 5px 0; font-size:14px;">Preview:</p>
<button onclick="toggleContrast()" style="width:93%; padding:5px; cursor:pointer;">Kontras Tinggi</button>
</div>
<div>
<p style="margin:0 0 5px 0; font-size:14px;">Navigation Speech Menu:</p>
<button id="voiceToggleBtn" onclick="RcntoggleVoiceNavigation()" style="width:93%; padding:5px; cursor:pointer; background:#dc3545; color:#fff; border:1px solid #dc3545; border-radius:4px; font-weight:bold;">Speech Turn Off ❌</button>
</div>
</div>
<style>
.high-contrast, .high-contrast *, .high-contrast div, .high-contrast article, .high-contrast section {
background: #000 !important;
color: #fff !important;
border-color: #fff !important;
}
.high-contrast a {
color: #ffff00 !important;
text-decoration: underline !important;
}
</style>
<script>
var isVoiceActive = false;
function speakText(message) {
if ('speechSynthesis' in window) {
window.speechSynthesis.cancel();
var speech = new SpeechSynthesisUtterance(message);
speech.lang = 'id-ID';
speech.rate = 1.1;
speech.pitch = 1;
window.speechSynthesis.speak(speech);
}
}
function RcntoggleVoiceNavigation() {
var btn = document.getElementById("voiceToggleBtn");
isVoiceActive = !isVoiceActive;
if (isVoiceActive) {
btn.innerText = "Suara Aktif ";
btn.style.background = "#28a745";
btn.style.borderColor = "#28a745";
speakText("Menu navigation sound enabled. Hover your cursor over the blog menu.");
} else {
btn.innerText = "Suara Mati ❌";
btn.style.background = "#dc3545";
btn.style.borderColor = "#dc3545";
if ('speechSynthesis' in window) {
window.speechSynthesis.cancel();
}
}
}
function RcntoggleAccMenu() {
var menu = document.getElementById("accMenu");
if (menu.style.display === "none") {
menu.style.display = "block";
if (isVoiceActive) speakText("Menu aksesibilitas terbuka");
} else {
menu.style.display = "none";
if (isVoiceActive) speakText("Accessibility menu closed");
}
}
function changeFontSize(action) {
var elements = document.querySelectorAll("p, span, article, entry-content, post-body, a");
elements.forEach(function(el) {
if (!document.getElementById("accMenu").contains(el)) {
var currentSize = parseFloat(window.getComputedStyle(el, null).getPropertyValue('font-size'));
if (action === 'increase') {
el.style.fontSize = (currentSize + 2) + 'px';
} else if (action === 'reset') {
el.style.fontSize = '';
}
}
});
if (isVoiceActive) {
action === 'increase' ? speakText("Text size increased") : speakText("Normal text size");
}
}
function toggleContrast() {
document.body.classList.toggle("high-contrast");
if (isVoiceActive) {
document.body.classList.contains("high-contrast") ? speakText("High-contrast mode is active.") : speakText("High-contrast mode off");
}
}
document.addEventListener("DOMContentLoaded", function() {
var interactiveElements = document.querySelectorAll("a, button, input[type='button'], input[type='submit'], input[type='text']");
interactiveElements.forEach(function(el) {
el.addEventListener("mouseenter", function() {
if (isVoiceActive) {
var textToRead = el.innerText || el.textContent || el.getAttribute("aria-label") || el.placeholder;
if (textToRead && textToRead.trim() !== "" && !document.getElementById("accMenu").contains(el)) {
speakText(textToRead.trim());
}
}
});
el.addEventListener("mouseleave", function() {
if (isVoiceActive && 'speechSynthesis' in window) {
window.speechSynthesis.cancel();
}
});
});
});
window.onclick = function(event) {
var menu = document.getElementById("accMenu");
var btn = document.getElementById("accBtn");
if (event.target != menu && event.target != btn && !menu.contains(event.target)) {
if (menu.style.display === "block") {
menu.style.display = "none";
if (isVoiceActive) speakText("Accessibility menu closed");
}
}
}
</script>
Design Concept
- When the user clicks the red "Mute ❌" button, Google/Apple technically counts this as the first valid interaction, allowing the audio system to activate immediately and smoothly without running afoul of privacy rules.
- Casual visitors will not be disturbed or subjected to noise, as the audio feature is opt-in (users must consciously activate it if they need it).
Those are a few scripts we’re sharing with you all today—feel free to use them completely free of charge. We do generate revenue from AdSense here—as you can see from the ads on this blog—so visits contribute to the RPM (which is quite low), while clicks count toward the CPM (which is decent). However, earnings from the Indonesian region aren't particularly high; still, this serves as one of the income sources used to maintain the blog and its domain.
Original Indonesian Language Here
Hope you find this useful!



Post a Comment for "How to Add Text-to-Speech and Accessibility Features to a Website (Free, Without Third-Party APIs)"