npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@jailson/say-my-name

v0.1.1

Published

A tiny web component that shows people how to pronounce your name: your own recording, a dictionary-style phonetic spelling, and optional speech synthesis.

Downloads

46

Readme

say-my-name

A tiny web component that shows people how to pronounce your name — your own recording, a dictionary-style phonetic spelling, and speech synthesis when there is no recording.

One tag. No account, no backend, nothing phoning home. About 4.4 KB gzipped, zero dependencies.

<script type="module" src="https://cdn.jsdelivr.net/npm/@jailson/say-my-name@0"></script>

<say-my-name audio="/audio/my-name.opus" respell="zhah-EEL-sown" ipa="ʒaˈiwsõ" lang="pt-BR">
  Jailson
</say-my-name>

Live demo and docs · Record or upload your name

Why this exists

Every existing option makes you depend on someone else's server for a 40 KB recording of your own voice. NameCoach is account-gated enterprise SaaS. NameDrop is a hosted link. The WordPress and Drupal plugins are locked to those CMSs. Vocalizer.js, the closest open-source thing, has been unmaintained since 2016 and fetches a stranger's recording of your name from a third-party API.

This is the version that just works on a static site, hosts nothing, and asks for nothing.

Install

npm install @jailson/say-my-name
import '@jailson/say-my-name'; // registers <say-my-name>

Or use the CDN snippet above — no build step, no bundler.

The package is scoped because npm rejects the unscoped say-my-name as too close to an existing saymyname. The element is still <say-my-name>; only the install line and the CDN URL carry the scope.

A name can have more than one right answer

A Brazilian name in an English-speaking country has (at least) two legitimate pronunciations: the native one, and the one you actually answer to locally. Both are correct. Most tools make you pick one.

<say-my-name display="ruby">
  Jailson
  <script type="application/json">
    [
      { "label": "Portuguese", "lang": "pt-BR", "audio": "/audio/pt.opus",
        "respell": "zhah-EEL-sown", "ipa": "ʒaˈiwsõ" },
      { "label": "How I'm called here", "lang": "en-CA", "audio": "/audio/en.opus",
        "respell": "JAY-ill-sun" }
    ]
  </script>
</say-my-name>

You get one labelled button per pronunciation, and the written form follows whichever one is playing.

In display="ruby" the two written forms frame the name the way a dictionary entry does — respelling above, IPA below — and show drops either one: show="respell" for the reading aid without the notation, show="ipa" for the notation without a spelling that only helps English speakers. The JSON lives in a <script type="application/json"> block, which browsers never render — so if this component fails to load, the visitor still just sees your name.

Speech synthesis is the fallback, not the answer

This component will happily synthesize your name, and there are three things worth knowing before you rely on it:

  • Browsers cannot be told how to pronounce a word. SSML <phoneme> is unsupported across engines, and on macOS the markup is read aloud as literal text rather than stripped. Your IPA cannot reach the synthesizer.
  • The voice is whatever the visitor's device happens to have — different on Windows, macOS, Android and iOS, and missing entirely without a voice pack for your language.
  • Synthesis fails hardest on unusual names, which are exactly the names that need a pronunciation widget.

So the component does three things about it: tts-text lets you feed the synthesizer something other than the spelling; a synthesized button draws a dashed sound wave and says "(synthesized voice)" in its accessible label; and if the browser has no voice for the language, the button is not rendered at all rather than mispronouncing you confidently. That last rule holds at the moment of speaking too, not only when the button is drawn: an utterance with no matching voice falls to the system default, which reads a Brazilian name in English. The component stays silent instead.

A recording is the most faithful answer, not the only respectable one

A clean, isolated sample of your voice on a public page is exactly what voice-cloning models want, and declining to publish one is a reasonable position. Generating a clip from a text-to-speech tool and marking it synthetic is a perfectly good outcome — the component labels it honestly either way, which is the part that actually matters.

The studio shows the real component at the top of the page and lets you edit it by clicking it — the name, the respelling and the IPA are all editable where they appear. For the audio it does three things: record in the browser, load a file you made elsewhere, or press Speak it — which runs eSpeak NG locally to guess the IPA from the name and language, write a matching respelling, speak it, and tick synthetic for you. Nothing is uploaded in any of the three.

One engine runs in the tab — eSpeak NG — and the voice dropdown picks between its 104 voices. They are variants of one synthesizer rather than separate engines, so all of them are robotic, but the choice is baked into the file you download: unlike browser speech synthesis, the voice you pick is the voice every visitor hears. The other tools the studio mentions are ones you open yourself and bring a file back from; nothing but eSpeak runs here.

The IPA and respelling fill in by themselves from the name and the language, as you type — no button. The first derivation of a visit downloads eSpeak (~9 MB) and says so while it runs; every one after that is a ~250 ms local call. Speak it additionally generates the audio.

The language field starts on pt-BR and autocompletes over all 133 languages eSpeak supports, generated from the engine at build time so the list can never drift from what is actually installed.

Every field stops being overwritten the moment you write it yourself, and a recording or an uploaded file is never replaced by a regeneration.

Attributes

| Attribute | Default | What it does | | ---------- | -------------- | ------------ | | audio | — | URL of your recording. Fetched only when someone presses play. | | respell | — | Dictionary-style respelling for humans, e.g. zhah-EEL-sown. | | ipa | — | IPA, with or without slashes. Hidden from screen readers. | | lang | — | BCP 47 tag. Picks the speech voice; the button hides if none matches. | | tts | fallback | off · fallback (only without a recording) · on (both, as separate buttons). | | tts-text | the name | What the synthesizer is actually given. | | synthetic| absent | Marks an audio file as a synthesized voice, so its button is labelled like live synthesis. | | voice | best match | Preferred voice name, e.g. Luciana. | | rate | 0.9 | Speech rate. Names land better slightly slower. | | display | inline | none · inline · ruby (respelling above the name, IPA below) · tooltip. | | show | both | both · respell · ipa — which written forms appear at all. | | name | element text | Only needed when the element has no text of its own. |

The element fires a say-my-name:play event (bubbles, composed) with { pronunciation, kind } in its detail, where kind is 'audio' or 'tts'.

Playing

A press plays; a second press stops; a third starts the name over. While a button is sounding it carries data-playing, and a ring is drawn around it — sweeping in time with a recording, turning on the spot for synthesis, which has no duration to promise. Both respect prefers-reduced-motion.

Styling

say-my-name {
  --smn-color: #6b46c1;
  --smn-phonetic-color: #666;
  --smn-phonetic-size: 0.85em;
  --smn-respell-size: 0.5em;
  --smn-button-size: 0.9em;
  --smn-gap: 0.4em;
  --smn-focus: 2px solid #6b46c1;
  --smn-ring-size: 1.7em;    /* the progress ring drawn around a playing button */
  --smn-ring-opacity: 0.55;
}

say-my-name::part(button) { border-radius: 999px; }
say-my-name::part(ipa) { font-family: 'Doulos SIL', serif; }

Parts: name, respell, phonetics, ipa, controls, button, button-synth, label.

Accessibility

  • A real <button>, keyboard-reachable, with a focus ring that inherits from your page.
  • Labels read as sentences: "Hear how to pronounce Jailson — Portuguese (synthesized voice)".
  • IPA is aria-hidden; read aloud, the symbols come out as disconnected punctuation.
  • Play is a toggle button: aria-pressed follows what you can hear.
  • The playing animation respects prefers-reduced-motion.
  • Failures are announced through a polite live region rather than swallowed.
  • Without JavaScript the name is still just text. The component enhances; it never replaces.

Related: the W3C Pronunciation Task Force is standardizing spoken-presentation markup for exactly this class of problem.

Frameworks

It is a custom element, so it works anywhere HTML does. React 19+ passes props to custom elements natively. Vue needs compilerOptions.isCustomElement. Svelte and Astro need nothing.

To avoid a tag-name collision, register it yourself:

import { defineSayMyName } from '@jailson/say-my-name/element';
defineSayMyName('my-name');

Development

npm install
npm run build        # ESM + CDN bundle, and refreshes the docs demo
npm test             # unit tests
npm run test:browser # Playwright, real custom elements and audio
npm run size         # fails if the bundle outgrows its budget
npm run serve        # then open http://localhost:8080/docs/

Licence

The component is MIT. Everything in src/, and the published npm package, has no dependencies and contains no GPL code. Using <say-my-name> on your site carries no copyleft obligation of any kind.

The studio in docs/studio/ is GPLv3, because it bundles eSpeak NG for the "suggest from spelling" feature, and eSpeak NG is GPLv3. The studio is a separate program that happens to load the component; that boundary is why the component's licence is unaffected. See docs/studio/LICENSE.

The engine is not committed here — the build stages it out of node_modules, and the studio fetches it only when someone presses the button.