Auto Read: your page, read aloud on arrival
Auto Read lets a website decide what RiddR reads when someone opens a page. You mark the content, and visitors who use RiddR hear it straight away, without selecting text or pressing a button. It is especially helpful for people with visual difficulties.
Mark what should be read
Add the class RiddR to the element that holds the text. The name is case-sensitive.
<div class="RiddR">
Welcome to the Riverside Library. We are open today from nine to six.
</div>
When the page has finished loading, RiddR reads the text of that element aloud. It uses the visitor’s own RiddR setup: their voice, language, speed and pronunciation rules.
What your visitors need
- RiddR installed in Chrome.
- Auto read switched on in RiddR’s options. It is on by default.
- RiddR allowed to access your site. A fresh installation only looks at a page when its toolbar button is clicked; RiddR asks for access to websites when someone adds a custom keyboard shortcut.
For everyone else nothing happens, so the class is safe to ship to all visitors.
Good to know
- Mark one element per page. When several elements carry the class, RiddR 25.05 reads their
aria-labelattributes instead of their text. - Reading starts on the page’s
loadevent. A page that finishes loading almost instantly can fire that event before RiddR is ready, and is then not read in RiddR 25.05. - Auto Read is markup, not an API: a page cannot start, pause or stop RiddR with JavaScript. For a read-aloud button of your own, see SpeakIt on your page.
Build your own TTS engine
RiddR is modular. SpeakIt, the voice it ships with, is one text-to-speech engine among many, and there are two ways to add yours.
Already have a Chrome TTS engine?
RiddR lists every voice Chrome knows about. An extension that registers a voice through Chrome’s ttsEngine API shows up in RiddR’s list of engines on its own. There is nothing to integrate.
Embed an engine in RiddR
To make an engine part of RiddR itself, add a JavaScript module to js/TTS/ in the source. RiddR handles the popup, shortcuts, options and settings; your module only turns text into speech.
Start from the Dummy engine
The Dummy engine is the smallest engine that works. It reports that reading started, writes Hello, World! to the console and reports that reading ended. Save it as js/TTS/MyEngine.js, rename it and replace the body of speak():
class Dummy
{
// TTS Engine name, it has to match the voice_name registered in manifest.json
name = 'Dummy'
// TTS Engine default parameters
parameters =
{
gender : null, // use null for not supported
pich : { min: 0, max: 2, default: 1.0 },
rate : { min: 0.1, max: 10, default: 1.0 },
volume : { min: 0, max: 1, default: 1.0 },
lang : [ 'en', 'en-US', 'en-GB', 'en-AU', /* ... */ 'mk', 'it' ],
default_lang : 'en-US'
}
speak ( utterance, options, TTS_response )
{
TTS_response( { type: 'start', charIndex: 0 } );
console.log( 'Hello, World!' ); // your speech synthesis goes here
TTS_response( { type: 'end', charIndex: utterance.length } );
}
stop () { /* stop reading */ }
pause () { /* pause logic */ }
resume () { /* resume logic */ }
volume ( volume ) { /* volume set logic */ }
}
export default new Dummy();
nameis the engine’s name. It has to match thevoice_nameyou register inmanifest.json.parametersdescribes what the engine supports: ranges forrate, pitch (pich) andvolume,nullfor anything unsupported, the language codes inlangand adefault_lang. RiddR’s options offer the languages you list.speak( utterance, options, TTS_response )is called when someone asks your voice to read.utteranceis the text,optionscarries Chrome’s speech options such aslang,rate,pitchandvolume, andTTS_responseis the callback for reporting progress.stop(),pause()andresume()are called when reading is stopped, paused or resumed.volume( volume )is reserved for volume changes while reading.
Report what the engine is doing
Chrome expects every engine to say when it starts and when it is done. Call TTS_response with an event object:
// reading started
TTS_response( { type: 'start', charIndex: 0 } );
// optional progress
TTS_response( { type: 'sentence', charIndex: 42, length: 18 } );
// finished
TTS_response( { type: 'end', charIndex: utterance.length } );
// failed
TTS_response( { type: 'error', errorMessage: 'What went wrong' } );
RiddR’s popup follows these events, so always finish with end or error.
Register and try it
Two small edits make Chrome and RiddR aware of the engine. First import the module in js/modules/TTS.js and add it to the embedded engines:
import SpeakIt from '../TTS/SpeakIt.js';
import MyEngine from '../TTS/MyEngine.js';
// ...inside the TTS class
embed = { SpeakIt : SpeakIt, MyEngine : MyEngine };
Then declare the voice in manifest.json, next to SpeakIt:
"tts_engine":
{
"voices":
[
{
"voice_name": "SpeakIt",
"remote": true,
"event_types": ["start", "marker", "sentence", "end", "error"]
},
{
"voice_name": "MyEngine",
"event_types": ["start", "end", "error"]
}
]
}
Add "remote": true if the engine sends text to a server, and list only the event types it really sends.
Try it
- Clone github.com/riddr/RiddR, open
chrome://extensions, switch on Developer mode and choose Load unpacked. - Reload the extension there after every change.
- Open RiddR’s options and pick your engine under Text to Speech Engine. Embedded engines are listed first.
- Select some text on any page and click the RiddR button. The Dummy engine answers in the console of the extension’s service worker.
A registered engine is an ordinary Chrome voice: other extensions and web pages can use it too, through chrome.tts and the Web Speech API.
Playing audio
Engines run in the extension’s service worker, where there is no audio output. RiddR keeps an offscreen reader for that. Hand it one audio URL per chunk of text and it plays them in order, preloads the next chunk, retries a failed download and sends start, sentence, end and error to Chrome for you. This is how SpeakIt works.
import IO from '../facades/io.js';
class MyEngine
{
name = 'MyEngine'
speak ( utterance, options, TTS_response )
{
IO.dispatch( 'audio',
{
action : 'speak',
data :
{
urls :
[
'https://example.com/speech/1.mp3',
'https://example.com/speech/2.mp3'
],
options : options,
utterance :
{
raw : utterance,
safe : [ 'First chunk of the text.', 'Second chunk.' ]
},
media : { header : 'Now reading', title : 'Page title' }
}
}, 'reader' );
}
stop () { IO.dispatch( 'audio', { action : 'stop' }, 'reader' ); }
pause () { IO.dispatch( 'audio', { action : 'pause' }, 'reader' ); }
resume () { IO.dispatch( 'audio', { action : 'resume' }, 'reader' ); }
}
safe holds the text chunks, one per URL and in the same order; the reader uses their lengths to report how far it has read. options.rate and options.volume are applied to the audio, and media labels Chrome’s media controls.
SpeakIt on your page: one familiar browser API
RiddR registers a text-to-speech engine named SpeakIt. In supported Chrome environments, that voice appears in speechSynthesis.getVoices(), alongside the browser’s other voices. A normal webpage can select it and start reading after a user clicks a button.
You do not need a RiddR JavaScript SDK or an extension-messaging bridge. The playground on the home page uses the same Web Speech route.
A voice, not an installation check
Web Speech exposes a voice’s name and capabilities, not the ID of the extension that supplies it. Finding a voice named SpeakIt means that voice is available; it does not conclusively identify a RiddR installation. If it is missing, offer another available voice or a link to install RiddR.
Quick start
Use the actual voice object returned by the browser. Voices may load asynchronously, so check once on startup and again when voiceschanged fires. Keep the call to speak() inside a user-initiated action.
<button id="read-aloud" type="button" disabled>
Read aloud
</button>
<p id="voice-status" role="status">Checking voices…</p>
Run this JavaScript after the button and status element exist:
const button = document.querySelector('#read-aloud');
const status = document.querySelector('#voice-status');
if (!('speechSynthesis' in window)) {
status.textContent = 'Speech is unavailable in this browser.';
} else {
const synth = window.speechSynthesis;
let voice;
function refreshVoice() {
voice = synth.getVoices().find(v =>
v.name === 'SpeakIt' && v.localService === false
);
button.disabled = !voice;
status.textContent = voice
? 'SpeakIt is ready.'
: 'SpeakIt is unavailable. Try another voice or install RiddR.';
}
refreshVoice();
synth.addEventListener('voiceschanged', refreshVoice);
button.addEventListener('click', () => {
if (!voice) return;
const speech = new SpeechSynthesisUtterance(
'Listen to the web.'
);
speech.voice = voice;
speech.lang = 'en-US';
speech.onstart = () => status.textContent = 'Reading…';
speech.onend = () => status.textContent = 'Finished.';
speech.onerror = event => {
if (event.error !== 'canceled' && event.error !== 'interrupted') {
status.textContent = 'Playback failed. Try another voice.';
}
};
synth.cancel();
synth.resume();
synth.speak(speech);
});
}
This example deliberately leaves the button disabled if SpeakIt is unavailable. A fuller player can offer the other voices returned by getVoices(). Let people choose their fallback instead of silently changing the service reading their text.
Playback controls
speechSynthesis.pause(); // Pause the current reading
speechSynthesis.resume(); // Continue reading
speechSynthesis.cancel(); // Stop and clear queued speech
Connect these calls to clearly labeled controls. Pause and resume work with the tested SpeakIt engine, but it does not forward standard Web Speech pause/resume events. Track your own button actions instead of relying only on onpause and onresume to update the interface. Handle start, end, cancellation, and errors separately.
Keep readable text available and make controls keyboard-accessible.
Languages and voices
SpeakIt’s voice can have an empty lang property. Set the utterance language yourself using a code accepted by the engine. For example, use en-US for American English, es for Spanish, and de for German. The current whitelist does not accept es-ES or de-DE.
See the language reference for all accepted codes. Other browser voices have their own language support; do not apply the SpeakIt whitelist to every voice.
Direct engine playback is different
This route calls SpeakIt directly. It does not apply the RiddR toolbar’s saved pronunciation rules, automatic language detection, or stored voice settings. SpeakIt uses an online speech provider, so it needs an internet connection and sends the text being read to that provider. Explain that choice to listeners; see our privacy policy.
Build with us
Explore the engine, report a reproducible issue, or help improve the reading experience. When reporting a voice problem, include your browser version, operating system, selected voice, language code, and a short non-sensitive example.
Browse the code
The extension’s public source and contribution history.
Report an issue
Share a bug or suggest an improvement on GitHub.
Checked against RiddR 25.05 (source revision e0e431f) in Chrome: Auto Read and the engine interface on October 5, 2026, the Web Speech route on October 1, 2026. Voice availability varies by browser and device.