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/tongue

v3.2.0

Published

On-device language identification for short text, across 84 languages. Pure JavaScript: no wasm, no inference runtime.

Readme

tongue — JavaScript / TypeScript SDK

On-device language identification for short text, across 84 languages. Runs in the browser and in Node from one import.

import { Tongue } from "@desert-ant-labs/tongue";

const tongue = await Tongue.load();
tongue.detect("kann ich das haben").language;   // "de"
tongue.detect("안녕하세요").language;             // "ko"

No wasm, no inference runtime, no dependencies. A detection is an int8 embedding gather, a sum, one 59×32 matmul and a masked softmax — a few thousand multiply-adds in plain JavaScript. That is why this package has a single entry point where emo-js needs separate browser and Node builds: there is no runtime to swap, only two ways to read 2 MB of weights.

Install

npm i @desert-ant-labs/tongue

Node ≥18, or any modern browser.

// Node: reads the bundled weights from the package directory.
const tongue = await Tongue.load();

// Browser: serve tongue_int8.bin and tongue_meta.json and point at them.
const tongue = await Tongue.load({ from: "/models/tongue" });

// Or supply the bytes yourself.
const tongue = Tongue.fromBytes(metadata, weightBytes);

Saying "I don't know"

Short input is often genuinely undecidable, and the SDK says so rather than guessing. Reliability is keyed off evidence — input length and how far the top candidate leads the runner-up — not raw softmax confidence, which is badly overconfident on two words.

const detection = tongue.detect("la casa");
detection.isTooCloseToCall;   // true — equally Italian and Spanish
detection.reliability;        // "tentative"
detection.candidates;         // [it 0.31, es 0.29, …]

Present both when isTooCloseToCall is set. Treat "tentative" as "unknown" rather than as an answer, and ask for more text where the product allows it.

API

| | | |---|---| | Tongue.load(options?) | load the model; options.from is a directory or base URL | | Tongue.fromBytes(metadata, bytes) | load from bytes you already have | | detect(text, topK?) | Detection | | Detection.language | top candidate, or null on empty input | | Detection.candidates | Prediction[] with probabilities | | Detection.reliability | "confident" · "likely" · "tentative" · "empty" | | Detection.isTooCloseToCall | top two ranked within 0.12, whatever topK is | | Detection.route.verdict | "decisive" (script alone settled it) · "narrowing" · "ambiguous" |

normalize, route, fnv1a and buckets are also exported, for anyone reproducing the feature pipeline.

CommonJS and TypeScript

The package is ESM-only. require() of it works natively on Node 20.19+, so a CJS server needs no changes at runtime. TypeScript in a CJS project should set "moduleResolution": "nodenext" (TS 5.8+) — the older node16 setting rejects the import with TS1479 even though the emitted require() runs. On Node older than 20.19, use a dynamic import().

Serving the model in a browser

On Node the weights load out of the package with no configuration. A bundler does not serve files from node_modules, so in a browser you serve the two model files yourself and point load at them:

const tongue = await Tongue.load({ from: "/models/tongue" });

Both files are exported, so a bundler can fingerprint and hash them rather than needing a copy step:

import binUrl from "@desert-ant-labs/tongue/model/tongue_int8.bin?url";   // Vite
import metaUrl from "@desert-ant-labs/tongue/model/tongue_meta.json?url";

Or copy node_modules/@desert-ant-labs/tongue/dist/tongue_{int8.bin,meta.json} into your static directory as a build step.

Calling Tongue.load() with no from in a browser resolves against the page URL, which on a single-page app hits the history fallback and returns index.html. The error says so explicitly if it happens.

The cross-platform contract

The normalizer, hasher and router are a frozen specification shared with the Python reference and the Swift and Kotlin SDKs. test/golden.test.js replays the same vector files all of them do, including whole-bag hasher equality:

npm test

Regenerate the Unicode tables rather than editing them:

python3 scripts/gen_ts_tables.py --reference ../tongue-training

Model

Weights, benchmarks and limitations: huggingface.co/desert-ant-labs/tongue. Live demo: desert-ant-labs-tongue-demo.static.hf.space.

Read the model card's failure modes before deploying: one or two words is often genuinely ambiguous, Malay and Indonesian are not reliably separable at this size, Mongolian works only in the traditional script, and brand names and version strings are not language at all.

Licence

Desert Ant Labs Source-Available Licence. Free for most apps; a commercial licence is required at scale.