Cross-platform Web Speech API (speech synthesis) button
Tags: webdev, javascript, showdev
Published: 2020-04-11 06:30:23

I like [web speech API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Speech_API/Using_the_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.

```js
window.speechSynthesis.getVoices()
```

## An approach to cross platform web speech API

```ts
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
    speechSynthesis.speak(utterance)
  }
}
```

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.

```js
document.querySelectorAll('script').forEach((script) => {
  script.replaceWith(script.cloneNode(true))
})
```

## A lesson from lazily creating `*.html`

In my mind, a simplest SSG is not Eleventy, but [parcel.js](https://parceljs.org/), 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.

```html
<meta name="viewport" content="width=device-width, initial-scale=1.0">
```

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)

```pug
    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")
```

## GitHub Pages vs Now.sh (aka Zeit.co) vs Netlify

For simple websites, Now.sh 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, now.sh URLs are ridiculously short (just like [js.org](https://js.org/)).

## Test

<iframe src="https://speak-btn.now.sh/btn?q=你好&lang=zh"
  style="width: 20px; height: 20px;"
  frameborder="0" allowtransparency="true">

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

## The project

The website for creation is here -- https://speak-btn.now.sh

{% github patarapolw/speak-btn no-readme %}