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

@davidmokos/react-use-transcription

v0.0.4

Published

A zero-configuration React hook that captures microphone audio, streams it to a Cloudflare Worker, and delivers real-time partial and final transcripts powered by OpenAI Whisper.

Downloads

34

Readme

@scope/react-use-transcription

A zero-configuration React hook that captures microphone audio, streams it to a Cloudflare Worker, and delivers real-time partial and final transcripts powered by OpenAI Whisper.

  • ✅ Works over a single secure WebSocket
  • ✅ Handles microphone permissions, buffering, and clean-up for you
  • ✅ Ships with sensible fallbacks and human-friendly error messages
  • ✅ Pairable with the included Cloudflare Worker for drop-in backend transcription

Installation

# using bun
bun add @scope/react-use-transcription

# using npm
npm install @scope/react-use-transcription

# using pnpm
pnpm add @scope/react-use-transcription

The audio worklet is bundled with the package—no extra files to copy. The hook automatically injects it via a blob URL when you call startTranscribing.


Quick Start

import { useTranscription } from '@scope/react-use-transcription';

export function TranscriptionDemo() {
  const {
    transcriptionStatus,
    isRecording,
    isProcessing,
    startTranscribing,
    stopTranscribing,
    transcription,
    partial,
    permissionState,
    error,
    levels, // 10 normalized audio levels for visualisations
  } = useTranscription({ wsUrl: 'wss://your-worker.example.com/ws' });

  const busy = transcriptionStatus === 'connecting' || isProcessing;

  return (
    <div>
      <button
        onClick={isRecording ? stopTranscribing : startTranscribing}
        disabled={busy || permissionState === 'denied' || permissionState === 'unsupported'}
      >
        {isRecording ? 'Stop Recording' : busy ? 'Processing…' : 'Start Recording'}
      </button>

      <p>Status: {transcriptionStatus}</p>

      <pre>{transcription}{partial ? `\n${partial}` : ''}</pre>

      <div style={{ display: 'flex', gap: 4, alignItems: 'flex-end', height: 40 }}>
        {levels.map((value, index) => (
          <div
            key={index}
            style={{
              flex: 1,
              background: '#0af',
              opacity: 0.6,
              height: `${Math.max(value, 0.05) * 100}%`,
              transition: 'height 80ms ease-out'
            }}
          />
        ))}
      </div>

      {error && (
        <p style={{ color: 'crimson' }}>
          {error.userMessage} <small>({error.code})</small>
        </p>
      )}
    </div>
  );
}

Serve the page over HTTPS or localhost, otherwise browsers will block microphone access.


Hook API

useTranscription(options) accepts:

| Option | Type | Default | Description | |-------------|----------------------|---------|----------------------------------------------| | wsUrl | string | — | WebSocket endpoint exposed by the worker. | | sampleRate| 16000 \| 48000 | 16000 | Desired PCM sample rate; must match backend. |

It returns:

| Field | Type | Notes | |------------------|-----------------------------------------|---------------------------------------------------------------------------------------| | transcriptionStatus | 'idle' \| 'connecting' \| 'recording' \| 'processing' | High-level phase for UI state machines. | | status | 'idle' \| 'connecting' \| 'recording' \| 'processing' | Alias for transcriptionStatus (kept for backwards compatibility). | | isTranscribing | boolean | true while a session is active (connecting, recording, or finalising). | | isRecording | boolean | true while the microphone is open and frames are being streamed. | | isProcessing | boolean | true after stopTranscribing until the backend sends its final transcript. | | startTranscribing | () => Promise<void> | Opens mic, worklet, WebSocket connection. Safe to call repeatedly. | | stopTranscribing | () => Promise<void> | Flushes buffers and lets the worker close the socket after the final transcript. | | transcription | string | Accumulated final transcripts. | | partial | string \| undefined | Latest interim status ("Listening…" or "Processing transcription…"). | | permissionState| 'granted' \| 'denied' \| … | Mirrors PermissionStatus. | | error | TranscriptionError \| null | Rich error with type, code, message, and userMessage for UI display. | | levels | number[] | 10-sample rolling audio intensity (0–1) for animated meters or visualisers. |

transcriptionStatus, isRecording, and isProcessing make it easy to tailor your UI (e.g. show a spinner while finalising or disable buttons during setup) without guessing from partial transcript strings.

Error Handling

The hook normalises browser-specific microphone errors. Display error.userMessage to end users and inspect error.code for programmatic flows (retry prompts, custom tooltips, etc.).


Deploying the Cloudflare Worker

The worker in apps/worker exposes the /ws endpoint consumed by the hook. Deploy it to your Cloudflare account with the following steps:

  1. Install dependencies (once per repo)

    bun install
  2. Configure Wrangler

    cd apps/worker
    wrangler login
  3. Provide secrets Use Wrangler secrets for production and apps/worker/.dev.vars for local development:

    wrangler secret put OPENAI_API_KEY        # required for OpenAI Whisper
    wrangler secret put ELEVEN_API_KEY       # required when TRANSCRIBER=elevenlabs
    wrangler secret put SILENCE_THRESHOLD    # optional (defaults to 0.012)
    wrangler secret put ELEVEN_STT_MODEL     # optional (defaults to eleven_multilingual_v2)

    In the Cloudflare dashboard: Workers → your worker → Settings → VariablesAdd variable → choose Secret and enter the same keys.

    For local development create apps/worker/.dev.vars with matching entries:

    OPENAI_API_KEY=sk-...
    ELEVEN_API_KEY=sk-...
    SILENCE_THRESHOLD=0.01

    Wrangler automatically loads .dev.vars when you run bun run dev inside apps/worker.

  4. Deploy

    bun run deploy

    That script invokes wrangler deploy with the bundled worker.

  5. Verify locally (optional)

    bun run dev

    Wrangler will expose the worker on http://localhost:8787/ws, perfect for local testing with the example app.

Once deployed, grab the live WebSocket URL from the Wrangler output (something like wss://asr-ws.your-account.workers.dev/ws) and feed it to the hook's wsUrl option.

Silence Trimming

The worker removes frames that fall below a configurable energy threshold before batching audio for Whisper. Tune it via the SILENCE_THRESHOLD secret:

wrangler secret put SILENCE_THRESHOLD  # e.g. 0.01 keeps quiet speech, 0 disables trimming
  • Default: 0.012 (≈1.2 % of full-scale amplitude).
  • Set to 0 or false to disable trimming entirely.

Serving the Audio Worklet

Nothing extra to host—the package registers the audio worklet dynamically and streams PCM16 frames straight to the worker.


Transcription Providers

Set the TRANSCRIBER variable (defaults to openai) in wrangler.toml or as a plain text variable in the Cloudflare dashboard to switch between providers. When you specify a provider, the corresponding API key must be present—otherwise the worker throws a configuration error. If you leave it blank, the worker prefers OpenAI when that key is available, otherwise it uses ElevenLabs.

| Provider value | Requirements | Notes | |------------------|---------------------------------------|----------------------------------------------------| | openai (default)| OPENAI_API_KEY secret | Uses Whisper (model=whisper-1). | | elevenlabs | ELEVEN_API_KEY secret | Uses ElevenLabs STT (model_id=eleven_multilingual_v2 by default). |

Optional secrets:

  • DEFAULT_LANG – hint language for both providers.
  • ELEVEN_STT_MODEL – override the ElevenLabs model ID if needed.

Development Tips

  • Use bun run build at the repo root to rebuild all packages, including this hook.
  • The example app in examples/textarea-basic demonstrates a minimal integration and makes a great starting point for UI experiments.
  • If you extend the protocol, update both this hook and the worker to keep the frame schema aligned.

License

MIT © Scope