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

@desert-ant-labs/ear

v3.2.0

Published

On-device spoken language identification for JavaScript: name the language of a recording before you transcribe it. Runs in the browser (WebAssembly + LiteRT.js) and server-side in Node (native), from one import.

Downloads

5,452

Readme

@desert-ant-labs/ear

On-device spoken language identification for JavaScript. Takes a recording and names the language it is in, so an app can pick the right recognizer before it starts transcribing. Everything runs locally, so the audio never leaves the device or browser.

Two entries share one Ear API:

  • @desert-ant-labs/ear (default): a WebAssembly pipeline with LiteRT.js inference (XNNPACK-accelerated CPU by default, optional WebGPU), for the browser. It has no native dependencies, so a single import builds cleanly for every target of a multi-target bundler (Next.js, Remix, SvelteKit, Nuxt), including the browser bundle and the Client-Component SSR pass those frameworks render in Node. It is safe to import during server-side rendering, but LiteRT.js needs a browser (or Web Worker) to initialize, so Ear.load() runs inference only in the browser; calling it in plain Node throws an actionable error pointing you to /native.
  • @desert-ant-labs/ear/native: a prebuilt native core (LiteRT on Linux, Core ML on macOS), for server-side inference in Node. No @litertjs/core, no build tools, no flags. Import it from server-only code (API routes, server actions, plain Node scripts). Do not import it from a component that also renders in the browser.
# Browser (default entry):
npm i @desert-ant-labs/ear @litertjs/core

# Server-side inference in Node (/native entry) needs no extra install:
npm i @desert-ant-labs/ear

Use

import { Ear } from "@desert-ant-labs/ear";          // browser
// import { Ear } from "@desert-ant-labs/ear/native"; // Node

const ear = await Ear.load();                        // downloads once, then cached
const detection = await ear.identify(samples, 16000);

detection.language      // "pt"
detection.confidence    // 0.98
detection.isReliable    // true

ear.dispose();

samples is mono Float32Array at any rate; audio is resampled, and 16 kHz avoids the conversion.

Branch on isReliable, not on confidence

if (detection.isReliable) {
  transcribeWith(detection.language);
} else {
  askTheUser();            // or fall back to a general recognizer
}

isReliable is false when the top two candidates are too close to separate, and false for the Nordic languages, which the model confuses with each other confidently rather than uncertainly - so their probability does not reveal the problem and a threshold on confidence cannot catch it. The flag is decided in the model and crosses the boundary as a number, so every SDK reads the same verdict.

The threshold behind it was set by sweeping it against 162 recordings: of the answers above it, 98.5% route correctly, and on files in a language the primary recognizer supports, 100% do.

What it listens to

A file handed to a transcriber is not speech end to end, so Ear does not listen to it end to end either. It ranks candidate windows by how much of their loudness varies at syllable rate - speech rises and falls three to six times a second and has gaps between words, music sustains, silence does not vary at all

  • and listens to the three most speech-like.

Pass windows to change how many:

await ear.identify(samples, 16000, { windows: 5 });

Limits

  • Speech mixed under louder music is read correctly about 60% of the time. Choosing better windows does not help; the model cannot read it.
  • Nordic languages are not distinguished reliably, and isReliable is false for all of them rather than reporting one confidently.
  • Recordings shorter than thirty seconds get a single window, so there is nothing to average and the answer is less certain than the number suggests.
  • Multilingual recordings are reported as whichever language the chosen windows contain, not as a mixture.

Licence

See LICENSE.md. The weights are published separately at desert-ant-labs/ear and derive from openai/whisper-tiny (MIT), attributed there.