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

note-listener

v1.1.6

Published

simple pitch detection library for instruments using pitchy

Readme

note-listener

A lightweight JavaScript/TypeScript library for real-time musical note detection from microphone audio in the browser. Ideal for guitar practice, tuners, music apps, and live pitch visualization.

note-listener listens to audio input, detects the dominant pitch, and continuously emits the corresponding musical note.

Features

  • Real-time musical note detection (C0–B8)
  • Works with any microphone or sound card
  • Smooths jitter using internal buffering
  • Configurable frequency range and clarity threshold
  • Pure browser-based (Web Audio API)
  • Minimal dependencies (pitchy)

Installation

npm install note-listener
# or
yarn add note-listener

How it works

This library is event-based, not return-based.

  1. You start listening to the microphone
  2. The library continuously detects notes
  3. Each detected note is sent to you via a callback
  4. Listening continues until you explicitly stop it

There is no "single return value" — notes are streamed over time.

Usage

1. List available audio inputs (optional)

import { listAudioInputs } from "note-listener"

const inputs = await listAudioInputs()
console.log(inputs)
// [{ deviceId: "...", label: "Built-in Microphone" }, ...]

Note: Browsers require microphone permission before device labels appear:

await navigator.mediaDevices.getUserMedia({ audio: true })

2. Create a pitch listener

import { createPitchListener } from "note-listener"

const listener = await createPitchListener({
  deviceId: inputs[0].deviceId,
  minEnergy: 0.03,
  onNote: (note, frequency, clarity) => {
    console.log(
      `${note} (${frequency.toFixed(2)} Hz, clarity ${clarity.toFixed(2)})`
    )
  },
})

At this point, the microphone is not yet active and no audio is being analyzed. You are just configuring the listener.

3. Start listening

listener.start()

When start() is called, the browser activates the selected microphone, audio is analyzed in real time, and detected notes are emitted via onNote.

4. Stop listening

listener.stop()

This stops the microphone stream, disconnects audio nodes, and frees system resources. You should always call stop() when done.

Examples

Plain JavaScript

import { listAudioInputs, createPitchListener } from "note-listener"

await navigator.mediaDevices.getUserMedia({ audio: true })

const inputs = await listAudioInputs()

const listener = await createPitchListener({
  deviceId: inputs[0].deviceId,
  onNote(note, freq) {
    console.log(`Current note: ${note} (${freq.toFixed(1)} Hz)`)
  },
})

listener.start()

// Later...
// listener.stop()

Vue 3

<template>
  <div>
    <h3>Select audio input</h3>

    <select v-model="selectedId">
      <option disabled value="">-- choose input --</option>
      <option v-for="d in inputs" :key="d.deviceId" :value="d.deviceId">
        {{ d.label }}
      </option>
    </select>

    <button @click="start" :disabled="!selectedId">Start</button>
    <button @click="stop">Stop</button>

    <p>Last note: {{ lastNote || "—" }}</p>
  </div>
</template>

<script setup>
import { ref, onMounted, onBeforeUnmount } from "vue"
import { listAudioInputs, createPitchListener } from "note-listener"

const inputs = ref([])
const selectedId = ref("")
const lastNote = ref(null)
let listener = null

onMounted(async () => {
  await navigator.mediaDevices.getUserMedia({ audio: true })
  inputs.value = await listAudioInputs()
})

async function start() {
  if (listener) listener.stop()

  listener = await createPitchListener({
    deviceId: selectedId.value,
    onNote(note) {
      lastNote.value = note
    },
  })

  listener.start()
}

function stop() {
  if (listener) listener.stop()
}

onBeforeUnmount(stop)
</script>

API

createPitchListener(options)

| Option | Type | Default | Description | | ------------------ | ---------- | ----------- | ---------------------------------------- | | deviceId | string | undefined | Audio input device ID | | minEnergy | number | 0.03 | Minimum RMS energy required | | clarityThreshold | number | 0.88 | Minimum pitch clarity | | minFreq | number | 82 | Minimum frequency (Hz) | | maxFreq | number | 1319 | Maximum frequency (Hz) | | onNote | function | required | Called when a note is detected |

onNote callback

(note: string, frequency: number, clarity: number) => void

Called continuously while audio is being analyzed. note is a string like "A4", frequency is in Hz, and clarity is a value between 0 and 1 indicating pitch confidence.

listAudioInputs()

Returns a promise resolving to an array of available audio input devices:

[{ deviceId: string, label: string }]

Browser support

  • Works in modern Chromium, Firefox, and Safari
  • Requires user interaction to initiate microphone access
  • Not supported in Node.js or server-side environments

License

MIT © Donald Edwin