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

@shiihaa/breath-detection

v1.0.0

Published

Real-time breath detection using microphone energy analysis, spectral centroid classification, and BLE heart rate data. Works in browsers and Capacitor apps.

Readme

@shiihaa/breath-detection

Real-time breath cycle detection using microphone energy analysis, spectral centroid classification, and optional BLE heart rate data. Works in browsers and Capacitor apps.

By shii · haa — a breathwork and biofeedback app built by a physician.

Features

  • Microphone-based breath detection — detects inhale/exhale cycles from audio energy
  • Spectral centroid classification — distinguishes inhale (higher frequency, turbulent airflow) from exhale (lower frequency, laminar airflow)
  • Dual-path detection — threshold-based state machine for paused breathing + peak-based fallback for continuous breathing
  • Auto-recalibration — adapts to changing ambient noise every 10 seconds
  • Refractory period — prevents double-counting with configurable minimum cycle gap
  • Optional BLE heart rate input — accepts external HR data for enhanced analysis
  • TypeScript types included
  • Zero dependencies

Installation

npm install @shiihaa/breath-detection

Quick Start

import { BreathDetector } from '@shiihaa/breath-detection';

const detector = new BreathDetector();

// Listen for completed breath cycles
detector.onCycle((cycle) => {
  console.log(`Breath: ${cycle.inhaleMs}ms in, ${cycle.exhaleMs}ms out`);
  console.log(`Rate: ${(60000 / cycle.cycleMs).toFixed(1)} breaths/min`);
  console.log(`Method: ${cycle.method}`); // 'threshold' or 'peak'
});

// Listen for real-time phase changes
detector.onPhase((event) => {
  console.log(`Phase: ${event.phase}, Energy: ${event.energy.toFixed(2)}`);
});

// Start microphone
const ok = await detector.start();
if (!ok) { console.error('Mic access denied'); return; }

// Calibrate (user breathes for 6 seconds)
const cal = await detector.calibrate();
console.log(`Noise: ${cal.noiseFloor.toFixed(4)}, Breath: ${cal.breathMax.toFixed(4)}`);

// Start detection loop
detector.startDetection();

// Later: stop
detector.stop();

How It Works

Detection Pipeline

Microphone → FFT → Energy + Centroid → State Machine → Breath Cycles
                                              ↑
                                     Peak Counter (fallback)
  1. Audio Capture: Microphone via Web Audio API (getUserMedia)
  2. Spectral Analysis: FFT computes energy in 150–2500 Hz band + spectral centroid
  3. Energy Smoothing: EMA filter removes noise spikes
  4. State Machine: Tracks phases (active → silent → active → silent = 1 cycle)
  5. Peak Fallback: If energy never drops to silence (continuous breathing), counts energy peaks instead — two peaks = one breath cycle
  6. Centroid Classification: Higher centroid → inhale (turbulent "shii..."), lower centroid → exhale (laminar "...haa")
  7. Auto-Recalibration: Noise floor and breath max adapt every 10s

Inhale vs. Exhale Classification

The spectral centroid (frequency center of mass) differs between inhale and exhale:

| Phase | Airflow | Centroid | |-------|---------|----------| | Inhale | Turbulent, through nasal passages | ~800–2500 Hz | | Exhale | Laminar, relaxed | ~200–800 Hz |

This is a novel approach — to our knowledge, no other breathwork app uses spectral analysis for breath phase classification.

API Reference

new BreathDetector(options?)

| Option | Type | Default | Description | |--------|------|---------|-------------| | fftSize | number | 4096 | FFT window size | | smoothingAlpha | number | 0.25 | Energy EMA smoothing (0–1) | | minCycleGapSeconds | number | 2.5 | Minimum seconds between cycles | | minPhaseSeconds | number | 1.5 | Minimum seconds per breath phase | | thresholdFactor | number | 0.35 | Detection sensitivity (0=sensitive, 1=strict) | | freqLow | number | 150 | Low frequency bound (Hz) | | freqHigh | number | 2500 | High frequency bound (Hz) | | enableCentroid | boolean | true | Enable spectral centroid classification | | centroidThreshold | number | 40 | Hz difference for confident in/out labeling |

Methods

| Method | Returns | Description | |--------|---------|-------------| | start() | Promise<boolean> | Start microphone capture | | stop() | void | Stop capture, release resources | | calibrate() | Promise<CalibrationResult> | 6-second calibration (2s noise + 4s breath) | | startDetection() | void | Start the detection loop | | stopDetection() | void | Stop detection (keeps mic open) | | setHeartRate(bpm) | void | Feed external HR for enhanced analysis | | getState() | object | Current state (phase, energy, centroid, etc.) |

Events

| Event | Callback | Description | |-------|----------|-------------| | onCycle(cb) | (cycle: BreathCycle) => void | Full breath cycle detected | | onPhase(cb) | (event: BreathPhaseEvent) => void | Phase change (every tick) | | onCalibration(cb) | (result: CalibrationResult) => void | Calibration complete | | onEnergy(cb) | (energy, centroid) => void | Raw energy + centroid per tick |

BreathCycle

{
  inhaleMs: number;       // Inhale duration
  exhaleMs: number;       // Exhale duration
  holdInMs: number;       // Hold after inhale (0 if none)
  holdOutMs: number;      // Hold after exhale (0 if none)
  cycleMs: number;        // Total cycle duration
  peakEnergy: number;     // Peak energy (0–1)
  confidence: number;     // Detection confidence (0–100)
  labelSwapped: boolean;  // Whether in/out was corrected by centroid
  centroidA1: number;     // Spectral centroid phase 1 (Hz)
  centroidA2: number;     // Spectral centroid phase 2 (Hz)
  method: string;         // 'threshold' or 'peak'
  timestamp: number;      // Completion time
}

Use with Capacitor / iOS

On iOS, WKWebView's Web Audio API AnalyserNode returns garbage data with getUserMedia. Use our companion plugin @shiihaa/capacitor-audio-analysis for native AVAudioEngine audio capture, then feed the energy values to BreathDetector.

Background

This library was extracted from shii · haa, a breathwork and biofeedback app. The breath detection algorithm was developed to solve a specific challenge: real-time breath phase detection using only a smartphone microphone, without any wearable sensor.

The spectral centroid approach for inhale/exhale classification emerged from the observation that inhaled air creates turbulent flow (higher frequencies) while exhaled air creates laminar flow (lower frequencies) — a well-known principle in respiratory physiology that hadn't been applied to mobile breath detection before.

Related

License

MIT © Felix Zeller / shii · haa