You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Title: Explore text-to-speech / read-aloud for course lesson content #3649
From a discussion in Slack: read-aloud audio for lesson content would help learners who prefer listening: commuters, non-native English readers, and people on slow connections where video isn't practical. Screen reader users are already covered by existing text, so this serves a different audience, not an accessibility gap.
Two possible approaches
Browser-native (Web Speech API): free, no third-party data sharing, uses voices already on the learner's device. Quality varies by OS/browser, and voice coverage is thin to nonexistent for many non-English locales (e.g. Urdu, RTL scripts).
Server-side TTS service: much better, more consistent voices, but requires a third-party dependency, per-request billing, and a privacy review. This is a bigger lift on WordPress.org infrastructure.
Additional case for doing this
Shifting some lesson content from produced video to read-aloud text also lowers the cost of future updates as text can be corrected in minutes, while video requires reshooting. This maintenance argument may be the strongest case to lead with, alongside the accessibility/convenience angle.
Open question
Is this English-only, or does it need to cover translated content? The two answers lead to very different builds, given how limited non-English voice support currently is.
As an MVP, I recommend we start with English and then explore multilingual options.
Next step
Training Team member Azhar Ali offered to build a proof of concept: the same paragraph from one lesson rendered three ways: browser voice, a commercial API voice, and the existing video audio for reference. Plus one non-English (likely Urdu) sample. This would make the quality difference and locale-coverage tradeoff concrete rather than theoretical. He'll document exactly which service/voice/settings were used for the commercial sample, and note this is a demo only, not a proposal to install anything on WordPress.org infrastructure (that will require further development and coordination with Meta).
It is laid out as a real Learn lesson so the feature is shown in context. There are two lessons on the page on purpose:
A lesson with a video (Intro to Block Patterns), and
A text-only lesson with no video (Introduction to WordPress).
The second one matters, because a lot of Learn lessons are written text with no narration at all, and that is exactly where read-aloud helps most.
On both, the proposed feature is a "Listen to this lesson" button that reads the text using the browser's own voice. It is free, it plays entirely on the reader's device, and nothing is sent to any server. It has Play, Pause, Resume, Restart and Stop, a speed and a volume control, and a small timer, so it behaves like a real player.
Underneath, so the team can judge how far this is worth taking, the same passage is offered three ways:
The free browser voice, the same one the Listen button uses.
A commercial API voice (ElevenLabs), attached as pre-generated files, with a few male and female voices you can switch between. The exact service, voices and settings are written on the page so anyone can reproduce them.
The lesson's own narrator, for reference.
I also included an Urdu sample, because that is where the English-only versus all-locales decision actually bites. On a typical device the browser has no Urdu voice at all, and the page says so honestly when it cannot find one. The Urdu passage is the reviewed community translation of the Introduction to WordPress lesson from #3613, not machine translation.
What the comparison suggests, offered to weigh rather than decide:
Default to the free browser-native route. It is the only one that fits WordPress.org with no cost, no third-party dependency, no data leaving the device and no privacy review, and it ships as a small plugin.
For a few high-value lessons and for non-English, allow pre-generated static audio files, made once and committed like any other asset. Commercial quality where it matters, without a live paid API or billing on wp.org.
Keep the live commercial API only as the quality comparison, not as the thing we install.
And the strongest reason, which came up in the discussion: read-aloud text can be corrected in minutes, while a produced video cannot be corrected without reshooting, so shifting some of the load to read-aloud lowers the cost of every future update.
To be clear, this is a demonstration only. It is not a proposal to install anything on WordPress.org, which is a Meta decision. The commercial clips were made on a personal free-tier allowance purely to show the quality difference.
Happy to walk through it in a Training meeting, or to shape any part of it into a focused pull request if the team wants to pick a direction.
@softglazee Hey there, thanks for getting this together so quickly! I took a listen, and have some feedback to your points above <3
Agreed that the priority should be for text-only lessons as most video-lessons only have their transcripts below.
Can we simplify the controls further?
-- Instead of a slider for speed, have some typical presets .5x, 1x, 1.5x, 2x.
-- A button for volume
-- A slider for the lesson length so listeners can rewind or move forward
-- For voice selection, perhaps we can hide that in a hamburger menu top left and just have a max default of 4?
I absolutely love that we can potentially already supply this for non-English languages too!
Lastly, I'd love your recommendation for how we can get less robotic voice
@Piyopiyo-Kitsune Thank you, this is exactly the kind of feedback that makes a prototype better. All four control changes are in, and the page is updated:
Speed presets. Now 0.5x, 1x, 1.5x and 2x as buttons, no slider.
Volume as a button. It opens a small control with volume down, a slider, volume up, a live percentage and mute, so the main row stays simple but the level is still adjustable.
A position slider. Added, and it seeks properly rather than jumping to the nearest sentence. This one took the most work, and the reason is worth a line because it affects the decision rather than just the UI: the Web Speech API has no seek, no duration and no currentTime. It is fire and forget. So the slider is built rather than borrowed. The lesson is split into sentences, each is measured, and a seek starts the utterance partway into a sentence at a word boundary. Progress is then tracked with the API's word boundary events, so the bar follows real speech rather than a stopwatch. Nudging forward five seconds moves five seconds.
Voice in a hamburger menu, top left, four maximum. Done, and the way those four are chosen turned out to be the answer to your last question, so more on that below.
One thing I added beyond the list: every section on the page now uses the same controls, not just the two lesson players. The comparison rows, the language tester and the two commercial voice rows all share them, so the page no longer looks like two different products. The tester keeps its language picker, now inside the same menu.
On getting a less robotic voice
The short version: it is mostly not the browser, it is which voice the browser picked, and the default is usually one of the worst ones on the device.
Three generations of text-to-speech are still shipping on real machines, and speechSynthesis.getVoices() hands them back mixed together:
Formant (eSpeak, most Linux defaults). Generated from rules, no human recordings. This is the sound people mean by robotic.
Concatenative (Microsoft David and Zira Desktop, Apple's Compact voices). Recorded fragments stitched together. Audible joins, flat sentence melody.
Neural (Microsoft's Online (Natural) voices, Google's network voices, Apple's Siri voices). A model generates the waveform, so intonation is learned. This is the jump that stops it sounding robotic.
The voice flagged default is whatever the operating system set years ago, which is very often a legacy one. So the fix is free: rank the list and choose, instead of accepting the default. Promote names containing "Natural", "Neural", "Siri", "Premium" or "Enhanced", promote localService === false, and demote "Compact", "eSpeak", the "Desktop" suffix and the macOS novelty voices.
The four voices in the new menu are the top four on your own device by exactly that ranking, tagged where they qualify. On Windows 11 in Edge this surfaces the free Microsoft Natural voices, and the difference is larger than anything else we have discussed on this issue.
Two more things worth knowing:
SSML does not work here. The spec allows it, but no major browser parses SSML inside a SpeechSynthesisUtterance. Chrome, Edge and Safari read the tags out loud. So the usual "add <break> for pauses" advice fails on the browser route. It only works on server-side services.
Chunking does work, and is free. Speaking sentence by sentence instead of one long block gives the engine a clean prosodic reset, which produces real sentence-final falls and real pauses. The demo already does this, and it is most of the natural-sounding gain available without paying anyone.
The full write-up is at the bottom of the page, including a per-platform table of what "good" looks like on Windows, macOS, Android and Linux. One uncomfortable point from it is worth flagging here: Edge exposes noticeably better voices than Chrome on the identical Windows machine. So how robotic it sounds partly depends on a browser choice we do not control, which is a real argument for not relying on the browser route alone for the lessons that matter most.
The other two points
Agreed on prioritising text-only lessons. That is where there is no narration at all today, and it is also where text can be corrected in minutes while a video cannot.
On non-English, yes, and the honest version is that the browser route cannot do it on most devices. The Urdu sample shows that plainly, since a typical machine has no Urdu voice at all. So translated content realistically needs the pre-generated file route, which is still free of any live API.
Happy to walk through this in a Training meeting, or to start shaping it into a focused pull request if the team wants to pick a direction.
Thank you for the detailed report. For me, the simple and free browser-picked voice is enough.
Here are the main reasons:
No additional cost – The browser’s built-in Text-to-Speech functionality is free to use and does not require paid third-party services, licenses, or APIs.
No implementation or maintenance overhead – Since the functionality is already available in modern browsers and operating systems, there is little to no development effort required and no additional infrastructure to maintain.
Familiar experience for users with visual impairments – Many users who rely on assistive technologies are already accustomed to browser- and OS-level speech and screen-reading tools. Using the existing accessibility layer provides a familiar and predictable experience.
Good enough quality for its intended purpose – Modern browser and operating-system speech engines provide clear and understandable voice output that is perfectly suitable for reading website content. I did not found any big problems with the generated voice, except a little humming and background noise in some segments, but that is perfectly fine.
Works with users’ existing accessibility setup – Users can continue using their preferred browser, operating-system voices, language settings, speech speed, and other accessibility preferences instead of being forced into a custom solution.
Better consistency with accessibility tools – Native solutions generally integrate more naturally with established assistive technologies and user settings than a custom website-specific Text-to-Speech implementation.
No unnecessary dependency on external services – There is no reliance on third-party APIs, usage quotas, service availability, account management, or future pricing changes.
Better privacy – Website content does not need to be sent to an external Text-to-Speech provider for processing.
Scalable by default – There are no additional costs or technical limitations as the number of website visitors grows.
We can start with this option and revisit it later if we identify any specific requirements or use cases that would justify switching to one of the other proposed solutions.
@rjekic Thank you so much, yes this will work, and I am agree with your decision, but, can you help me to understand how can we proceed next like getting approval from meta team and then implementation of this in real or production ready etc. @Piyopiyo-Kitsune do you know the next process? or what are rules for making things production ready as per Meta or any other team rules?
A possible third option alongside the two listed: a self-hosted, open-weight TTS model. I use one daily. I work through WordPress VIP's Learn courses by ear with the Read Aloud browser extension pointed at Kokoro-82M, a small open-weight model (Apache 2.0) that I run on my own machine.
Why it may be worth adding to the proof of concept:
No per-request billing and no third-party data sharing, since it runs on infrastructure you control.
Voice quality is much closer to the commercial services than the browser voices, in my experience.
It's small enough to run on modest hardware, and audio can be generated once per lesson and cached, then regenerated only when the text changes. That fits the "text can be corrected in minutes" argument here.
Trade-off: it's another service to host and maintain, and language coverage is limited. [check Kokoro's current language list before posting]
Happy to produce a sample of the same paragraph for the three-way comparison.
@JBEnterprises thank you, a self-hosted open-weight model is a fair third option, and I had not tested one.
I checked Kokoro-82M: the weights are Apache 2.0, and v1.0 covers 8 languages (English, Spanish, French, Italian, Brazilian Portuguese, Hindi, Japanese and Mandarin Chinese). Urdu is not in that list, so the Urdu lesson in the prototype would still need the browser voice.
On direction, @rjekic's view above is that the free browser voice is enough, because there is nothing to host or maintain. A self-hosted model would sound better, but it brings back exactly that hosting and maintenance, which would be Meta's call. So I see it as a possible later upgrade for the main languages rather than the starting point.
If you share a sample of the same paragraph, I am happy to add it to the comparison on the page so the team can hear the difference.
@Piyopiyo-Kitsune any suggestions? or should we close it? I am seeing this is pending for long long time.
Activity
softglazee commentedon Aug 25, 2026
@Piyopiyo-Kitsune Great idea, thank you so much, let me know when it approve, I will start with prototype...
Piyopiyo-Kitsune commentedon Sep 2, 2026
Thanks @softglazee ! Please feel free to start working on the prototype 🙏
softglazee commentedon Sep 2, 2026
Okay @Piyopiyo-Kitsune I will update you within two or three days
softglazee commentedon Sep 7, 2026
@Piyopiyo-Kitsune here is the proof of concept, as promised.
Rather than describe it, I built a page you can actually try, so the difference is something you hear rather than read about:
https://softglazee.github.io/learn-read-aloud-poc/
It is laid out as a real Learn lesson so the feature is shown in context. There are two lessons on the page on purpose:
The second one matters, because a lot of Learn lessons are written text with no narration at all, and that is exactly where read-aloud helps most.
On both, the proposed feature is a "Listen to this lesson" button that reads the text using the browser's own voice. It is free, it plays entirely on the reader's device, and nothing is sent to any server. It has Play, Pause, Resume, Restart and Stop, a speed and a volume control, and a small timer, so it behaves like a real player.
Underneath, so the team can judge how far this is worth taking, the same passage is offered three ways:
I also included an Urdu sample, because that is where the English-only versus all-locales decision actually bites. On a typical device the browser has no Urdu voice at all, and the page says so honestly when it cannot find one. The Urdu passage is the reviewed community translation of the Introduction to WordPress lesson from #3613, not machine translation.
What the comparison suggests, offered to weigh rather than decide:
And the strongest reason, which came up in the discussion: read-aloud text can be corrected in minutes, while a produced video cannot be corrected without reshooting, so shifting some of the load to read-aloud lowers the cost of every future update.
To be clear, this is a demonstration only. It is not a proposal to install anything on WordPress.org, which is a Meta decision. The commercial clips were made on a personal free-tier allowance purely to show the quality difference.
Happy to walk through it in a Training meeting, or to shape any part of it into a focused pull request if the team wants to pick a direction.
Piyopiyo-Kitsune commentedon Sep 8, 2026
@softglazee Hey there, thanks for getting this together so quickly! I took a listen, and have some feedback to your points above <3
-- Instead of a slider for speed, have some typical presets .5x, 1x, 1.5x, 2x.
-- A button for volume
-- A slider for the lesson length so listeners can rewind or move forward
-- For voice selection, perhaps we can hide that in a hamburger menu top left and just have a max default of 4?
softglazee commentedon Sep 10, 2026
@Piyopiyo-Kitsune Thank you, this is exactly the kind of feedback that makes a prototype better. All four control changes are in, and the page is updated:
https://softglazee.github.io/learn-read-aloud-poc/
Speed presets. Now 0.5x, 1x, 1.5x and 2x as buttons, no slider.
Volume as a button. It opens a small control with volume down, a slider, volume up, a live percentage and mute, so the main row stays simple but the level is still adjustable.
A position slider. Added, and it seeks properly rather than jumping to the nearest sentence. This one took the most work, and the reason is worth a line because it affects the decision rather than just the UI: the Web Speech API has no seek, no
durationand nocurrentTime. It is fire and forget. So the slider is built rather than borrowed. The lesson is split into sentences, each is measured, and a seek starts the utterance partway into a sentence at a word boundary. Progress is then tracked with the API's wordboundaryevents, so the bar follows real speech rather than a stopwatch. Nudging forward five seconds moves five seconds.Voice in a hamburger menu, top left, four maximum. Done, and the way those four are chosen turned out to be the answer to your last question, so more on that below.
One thing I added beyond the list: every section on the page now uses the same controls, not just the two lesson players. The comparison rows, the language tester and the two commercial voice rows all share them, so the page no longer looks like two different products. The tester keeps its language picker, now inside the same menu.
On getting a less robotic voice
The short version: it is mostly not the browser, it is which voice the browser picked, and the default is usually one of the worst ones on the device.
Three generations of text-to-speech are still shipping on real machines, and
speechSynthesis.getVoices()hands them back mixed together:The voice flagged
defaultis whatever the operating system set years ago, which is very often a legacy one. So the fix is free: rank the list and choose, instead of accepting the default. Promote names containing "Natural", "Neural", "Siri", "Premium" or "Enhanced", promotelocalService === false, and demote "Compact", "eSpeak", the "Desktop" suffix and the macOS novelty voices.The four voices in the new menu are the top four on your own device by exactly that ranking, tagged where they qualify. On Windows 11 in Edge this surfaces the free Microsoft Natural voices, and the difference is larger than anything else we have discussed on this issue.
Two more things worth knowing:
SpeechSynthesisUtterance. Chrome, Edge and Safari read the tags out loud. So the usual "add<break>for pauses" advice fails on the browser route. It only works on server-side services.The full write-up is at the bottom of the page, including a per-platform table of what "good" looks like on Windows, macOS, Android and Linux. One uncomfortable point from it is worth flagging here: Edge exposes noticeably better voices than Chrome on the identical Windows machine. So how robotic it sounds partly depends on a browser choice we do not control, which is a real argument for not relying on the browser route alone for the lessons that matter most.
The other two points
Agreed on prioritising text-only lessons. That is where there is no narration at all today, and it is also where text can be corrected in minutes while a video cannot.
On non-English, yes, and the honest version is that the browser route cannot do it on most devices. The Urdu sample shows that plainly, since a typical machine has no Urdu voice at all. So translated content realistically needs the pre-generated file route, which is still free of any live API.
Happy to walk through this in a Training meeting, or to start shaping it into a focused pull request if the team wants to pick a direction.
rjekic commentedon Sep 14, 2026
@softglazee
Thank you for the detailed report. For me, the simple and free browser-picked voice is enough.
Here are the main reasons:
We can start with this option and revisit it later if we identify any specific requirements or use cases that would justify switching to one of the other proposed solutions.
cc @Piyopiyo-Kitsune @devmuhib009 @sumitsinghwp
softglazee commentedon Sep 14, 2026
@rjekic Thank you so much, yes this will work, and I am agree with your decision, but, can you help me to understand how can we proceed next like getting approval from meta team and then implementation of this in real or production ready etc. @Piyopiyo-Kitsune do you know the next process? or what are rules for making things production ready as per Meta or any other team rules?
JBEnterprises commentedon Sep 25, 2026
A possible third option alongside the two listed: a self-hosted, open-weight TTS model. I use one daily. I work through WordPress VIP's Learn courses by ear with the Read Aloud browser extension pointed at Kokoro-82M, a small open-weight model (Apache 2.0) that I run on my own machine.
Why it may be worth adding to the proof of concept:
Happy to produce a sample of the same paragraph for the three-way comparison.
softglazee commentedon Oct 19, 2026
@JBEnterprises thank you, a self-hosted open-weight model is a fair third option, and I had not tested one.
I checked Kokoro-82M: the weights are Apache 2.0, and v1.0 covers 8 languages (English, Spanish, French, Italian, Brazilian Portuguese, Hindi, Japanese and Mandarin Chinese). Urdu is not in that list, so the Urdu lesson in the prototype would still need the browser voice.
On direction, @rjekic's view above is that the free browser voice is enough, because there is nothing to host or maintain. A self-hosted model would sound better, but it brings back exactly that hosting and maintenance, which would be Meta's call. So I see it as a possible later upgrade for the main languages rather than the starting point.
If you share a sample of the same paragraph, I am happy to add it to the comparison on the page so the team can hear the difference.
@Piyopiyo-Kitsune any suggestions? or should we close it? I am seeing this is pending for long long time.