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

hear-my-site

v0.0.2

Published

Headless live browser audio transcription exposed through WebMCP.

Readme

hear-my-site

Headless, framework-agnostic live browser audio transcription for agents such as Codex through WebMCP. The package mixes browser audio tracks locally, sends the selected provider either mono PCM16 or one mixed MediaStreamTrack, and registers exactly three adopter-named tool roles on document.modelContext. Providers are Gemini 3.5 Transcribe Live, OpenAI GPT-Live-Transcribe, and Chrome Web Speech.

The package is browser-only ESM and requires Chrome 150+ with the current WebMCP imperative API. It has no components, CSS, framework hooks, WebMCP polyfill, deprecated alias, or no-WebMCP mode.

Install

pnpm add hear-my-site

Minimal integration

import { createHearMySite } from 'hear-my-site';

const hear = await createHearMySite({ credential: { type: 'api-key', value: userSuppliedKey } });
hear.addAudioSource(existingMeetingStream);

The constructor waits up to five seconds for document.modelContext. If the browser does not expose it, creation rejects with WEBMCP_UNAVAILABLE.

Complete API

const hear = await createHearMySite({
  credential,
  options: {
    provider: 'gemini',
    languageCode: '',
    mode: 'VERBATIM',
    customVocabulary: [],
  },
  webMcp: {
    tools: {
      start: { name: 'join_meeting', title: 'Join meeting', description: 'Join as the local meeting assistant.' },
      get: { name: 'follow_meeting', title: 'Follow meeting', description: 'Wait for the next meeting segment.' },
      stop: { name: 'leave_meeting', title: 'Leave meeting', description: 'Leave and release local assistant resources.' },
    },
    allowCredentialInput: true,
    transcriptWaitMs: 20_000,
  },
});

hear.setCredential(credential);
hear.clearCredential();

const sourceId = hear.addAudioSource(streamOrTrack, {
  id: 'remote-alice',
  gain: 1,
  owned: false,
});
hear.removeAudioSource(sourceId);

const unsubscribe = hear.subscribe((state) => render(state));
hear.getState();

await hear.start({ mode: 'SMART' });
const page = hear.getTranscript({
  afterSegmentId: 0,
  maxChars: 50_000,
  includeInterim: true,
});
await hear.stop();

unsubscribe();
await hear.destroy();

start(), getTranscript(), and stop() return a shared CommandResult shape. Expected conditions such as a missing credential, no active audio, a required user gesture, or an already stopped session are represented by stable codes rather than hidden fallbacks.

Stopping retains the completed transcript for later reads. Starting again creates a new session. destroy() stops the session, clears the in-memory credential, aborts all three WebMCP registrations, closes the audio graph, and releases every package-owned resource. A destroyed instance cannot be reused.

Credentials

Gemini and OpenAI accept a directly supplied browser API key or an ephemeral token. Chrome Web Speech requires no credential:

const hear = await createHearMySite({ options: { provider: 'web-speech' } });
hear.addAudioSource(existingMeetingStream);
await hear.start();

Select the provider in the transcription options; the package never silently switches providers.

const hear = await createHearMySite({
  credential: { type: 'api-key', value: userSuppliedOpenAiKey },
  options: { provider: 'openai' },
});

hear.setCredential({ type: 'api-key', value: anotherUserSuppliedKey });
hear.setCredential({ type: 'ephemeral-token', value: token });

An async provider is also available when an adopting site wants to retrieve fresh credentials from its own backend:

const hear = await createHearMySite({
  credential: async ({ provider, reason, connection }) => {
    const response = await fetch(`/api/${provider}-token`, {
      method: 'POST',
      headers: { 'content-type': 'application/json' },
      body: JSON.stringify({ reason, connection }),
    });
    const { token } = await response.json();
    return { type: 'ephemeral-token', value: token };
  },
});

For Gemini and OpenAI, the credential provider is called for the initial connection and again during automatic nine-minute connection rotation. The package does not write credentials to storage or include their values in surfaced errors.

Audio ownership and mixing

addAudioSource() accepts a MediaStream or live audio MediaStreamTrack. Multiple local and remote sources are mixed through one Web Audio graph, and sources may be added or removed while transcription is running.

Sources are borrowed by default. Borrowed WebRTC tracks remain live after stop() and can continue powering the meeting. Set owned: true only when the package should stop and remove the track on stop, as the Hear My Browser extension does for its tab-capture stream.

Each source accepts a gain from 0 to 4. Duplicate IDs, invalid gains, and inputs without a live audio track fail explicitly. Gemini receives 16 kHz PCM16 in 100 ms chunks, OpenAI receives 24 kHz PCM16 through WebRTC, and Chrome Web Speech receives one mixed MediaStreamTrack. Optional Web Speech language, punctuation, and vocabulary hints are applied when supported and otherwise ignored.

The package ships its AudioWorklet as a separate same-origin JavaScript asset instead of a data: URL, so applications can keep a strict script-src 'self' Content Security Policy.

Transcript cursors

getTranscript() defaults to 50,000 finalized characters and includes the current interim utterance. Use its cursor incrementally:

let cursor = 0;

const result = hear.getTranscript({ afterSegmentId: cursor });
if (result.ok) {
  consume(result.data.segments);
  cursor = result.data.cursor;
  if (result.data.hasMore) readAgainImmediately();
}

historyTruncated signals that the requested cursor predates retained local history. Transcript results carry untrustedContentHint: true in WebMCP because spoken material is data, not an instruction to Codex or another agent.

WebMCP tools

Creation always registers three roles—start, get, and stop—but the adopting site owns their public identity. Omit webMcp.tools to use the transcription-oriented defaults:

  • start_browser_transcription
  • get_browser_transcript
  • stop_browser_transcription

When customizing, provide all three names, titles, and descriptions. Names must be unique and contain at most 64 letters, numbers, underscores, or hyphens. Complete identity sets prevent a half-renamed surface whose tool names and descriptions disagree.

allowCredentialInput is false by default. Enabling it adds an optional API-key or ephemeral-token credential to the start role. The executor validates the credential, stores it only on the in-page session, and never includes it in results or errors. Use this only when Codex or another calling agent has its own approved secret source.

transcriptWaitMs enables cancellable long reads for the get role. When the cursor is current, the call waits for a finalized segment or session-state change, then returns immediately; the maximum is 30 seconds. A timeout still returns the latest interim text. This keeps a meeting assistant responsive without a fixed five-second polling loop.

The WebMCP executors call the same session object as the manual methods. All three honor the executor’s lifecycle signal; destroying the instance aborts registration and unregisters every tool.

When Hear My Browser is also installed, the native site registration owns the page. The extension automatically unregisters its default tools and yields to the site's configured tool set, so Codex and other agents never see duplicate tools or a session different from the site's state.

Defaults

| Setting | Default | | --- | --- | | Provider | gemini | | Language | Automatic detection ("") | | Mode | VERBATIM | | Vocabulary | [] | | Transcript page | 50_000 finalized characters | | Interim text | Included |

Security boundary

Raw audio travels directly from the adopting page to the explicitly selected transcription provider. Gemini and OpenAI accept direct browser API keys or ephemeral tokens; Chrome Web Speech requires no credential and Chrome determines whether recognition is local or remote. This package has no analytics, telemetry, remote logging, persistence, signaling, or developer-operated service. A website that issues ephemeral tokens is responsible for authenticating its own token endpoint and minting appropriately constrained tokens.

License

MIT