Quickstart: plain HTML
No build step. One script tag, one element.
1. Add the element
<textarea id="message"></textarea>
<wordink-mic for="message" endpoint="/api/wordink"></wordink-mic>
<script
type="module"
src="https://cdn.jsdelivr.net/npm/@wordink/web@0.1.0/dist/wordink-web.cdn.js"
integrity="sha384-lWbge/8tkdUwVOrb/hLdE3Paghx10JgJo4H3WEhznZWbZpd0ur7kOP9MFCRI/kUI"
crossorigin="anonymous"
></script>
for is the id of the field to dictate into: an <input>, a <textarea> or a contenteditable.
Hold the button, speak, release, and the text is inserted at the cursor.
The pinned version and the integrity hash
The script URL pins an exact version (@wordink/web@0.1.0), and integrity is the
Subresource Integrity
hash of that exact file, so the browser refuses to run it if the CDN ever serves different bytes.
SRI on a cross-origin script needs crossorigin="anonymous". The hash on this page is generated
from the release build when the docs are published. To compute or check it yourself:
curl -sL https://cdn.jsdelivr.net/npm/@wordink/web@0.1.0/dist/wordink-web.cdn.js \
| openssl dgst -sha384 -binary | openssl base64 -A
Prefix the output with sha384-. When you upgrade, change the version and the hash together.
The CDN build is one ES module (about 11 KB gzipped) with @wordink/core bundled in. It fetches the
core WebAssembly file (wordink_core_bg.wasm, about 35 KB gzipped) from beside itself. The
integrity attribute covers the script, not that .wasm fetch. If you want both under your control,
self-host them: copy dist/wordink-web.cdn.js and dist/wordink_core_bg.wasm from the
@wordink/web package into one directory on your site and point src at it.
2. Point it at your relay
endpoint="/api/wordink" is the URL of your @wordink/server relay. The relay holds
your provider key, so the key never reaches the page. It takes a few lines on Cloudflare Workers or
Node; the relay guide has the setup, including the authorize check it requires.
To switch providers, change one attribute: provider="groq" (the default), "openai" or
"deepgram". See the provider comparison.
Trying it before you have a relay
On localhost you can skip the relay and give the element a provider key directly:
<script type="module">
document.querySelector("wordink-mic").devKey = "gsk_…"; // localhost only
</script>
Remove the endpoint attribute when you do: the element takes one or the other. A dev key only works
when the page is served from localhost, 127.0.0.1 or [::1], and anyone who can open the page can
read it. Read the dev key warning before using one.
No key at all: the local engine
@wordink/local runs speech recognition in the browser, so it needs no key and no relay.
Its model runs in a Web Worker, and browsers only start workers from your own origin, so serve its two
files from your site. Make them once:
npm install @wordink/local@1.0.0 esbuild
npx esbuild node_modules/@wordink/local/dist/index.js node_modules/@wordink/local/dist/worker.js --bundle --format=esm --outdir=wordink-local
That writes wordink-local/index.js and wordink-local/worker.js. Next to them, create index.html:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Dictation</title>
</head>
<body>
<textarea id="message" rows="5"></textarea>
<wordink-mic for="message"></wordink-mic>
<script
type="module"
src="https://cdn.jsdelivr.net/npm/@wordink/web@0.1.0/dist/wordink-web.cdn.js"
integrity="sha384-lWbge/8tkdUwVOrb/hLdE3Paghx10JgJo4H3WEhznZWbZpd0ur7kOP9MFCRI/kUI"
crossorigin="anonymous"
></script>
<script type="module">
import { createLocalProvider } from "./wordink-local/index.js";
document.querySelector("wordink-mic").provider = createLocalProvider();
</script>
</body>
</html>
Serve the directory (for example npx serve . or python3 -m http.server 8000) and open it at
http://localhost:…. The first press downloads the model (about 32 MB); later visits load it from the
browser cache, and dictation then works offline. The page has to be a secure context (HTTPS or
localhost) because the model files are checksum-verified with WebCrypto.
Next
- Style it: CSS custom properties,
::part(button),::part(meter),::part(status), or your own button inside the element. All attributes, properties and events are in the@wordink/webREADME. - Rich editors (ProseMirror, Lexical, Slate): handle the cancelable
wordink-transcriptevent and insert the text yourself. - Clean up text with a
transformthat calls your own backend.