DEV Community

Pacharapol Withayasakpunt
Pacharapol Withayasakpunt

Posted on • Updated on

Cross-platform Web Speech API (speech synthesis) button

I like web speech API in that it can help assist with learning human languages.

But forget about mobile first, it's not even cross platform.

Try running this both on mobile and desktop.

Enter fullscreen mode Exit fullscreen mode

An approach to cross platform web speech API

const allVoices: Record<string, string> = {}
speechSynthesis.getVoices().map(v => {
  allVoices[v.lang] = v.lang
speechSynthesis.onvoiceschanged = () => {
  speechSynthesis.getVoices().map(v => {
    allVoices[v.lang] = v.lang

export function speak (s: string, lang: string) {
  const voices = Object.keys(allVoices)
  const stage1 = () => voices.filter((v) => v === lang)[0]
  const stage2 = () => {
    const m1 = lang.substr(0, 2)
    const m2 = lang.substr(3, 2)
    const r1 = new RegExp(`^${m1}[-_]${m2}`, 'i')
    return voices.filter((v) => r1.test(v))[0]
  const stage3 = () => {
    const m1 = lang.substr(0, 2).toLocaleLowerCase()
    return voices.filter((v) => v.toLocaleLowerCase().startsWith(m1))[0]

  lang = stage1() || stage2() || stage3() || ''

  if (lang) {
    const utterance = new SpeechSynthesisUtterance(s)
    utterance.lang = lang
Enter fullscreen mode Exit fullscreen mode

Basically, if you get the first two characters right, and the platform offers web speech API. You can "speak". For example, zh, ja and ru.

Integrating into SSG without modifying the JavaScript

An approach is using an IFrame, with frameborder="0" allowtransparency="true", if your SSG does not sanitize Markdown.

Actually, if you choose JavaScript path, it is harder, as <script> tags aren't run by default. You have to always activate it.

document.querySelectorAll('script').forEach((script) => {
Enter fullscreen mode Exit fullscreen mode

A lesson from lazily creating *.html

In my mind, a simplest SSG is not Eleventy, but parcel.js, but it is completely barebone; as you can parcel build src/*.html

Sadly, HTML is not for mobile by default. There is a minimally required tag, if you want it mobile-accessible.

<meta name="viewport" content="width=device-width, initial-scale=1.0">
Enter fullscreen mode Exit fullscreen mode

Otherwise, it will be very small on mobile. I also recommend <meta charset="UTF-8">. For VSCode, the Emmet is html:5.

Another simplest, but important thing, is social links aren't enabled by default. You might do something like, (at bare minimum)

    meta(name="description", content=description data-meta="description")

    meta(property="og:title" content=title data-meta="title")
    meta(property="og:description" content=description data-meta="description")
    meta(property="og:image" content=image data-meta="image")
    meta(property="og:url" content=url data-meta="url")

    meta(property="twitter:title" content=title data-meta="title")
    meta(property="twitter:description" content=description data-meta="description")
    meta(property="twitter:image" content=image data-meta="image")
Enter fullscreen mode Exit fullscreen mode

GitHub Pages vs (aka vs Netlify

For simple websites, and Neltify also offers staging URL for deploying to production. This makes sure I don't unpurposefully break the production as online env might be different from your local machine.

Also, URLs are ridiculously short (just like


style="width: 20px; height: 20px;"
frameborder="0" allowtransparency="true">

(Not working on I guess <script>, <style> and <iframe> are usually sanitized.)

The project

The website for creation is here --

GitHub logo patarapolw / speak-btn

Pluggable multi-lingual speak-button for both mobile and desktop

Top comments (0)