Skip to the live demo
v0.3.0Pretrained fastText and CLD3, packed small

Language identification in 78 kB

defuss-lid tells you which language a text is written in, right where the text is. Pretrained fastText and Google CLD3 models, compressed into small language packs, run in your process or browser tab: no inference service, no API key, no text sent anywhere.

  • Node.js
  • Bun
  • Browsers

78 kB is the der_3 pack: German, English and Russian in 78.0 kB. Twenty languages fit in 110.4 kB, all 109 CLD3 labels in 130.4 kB.

78.0 kBpack file for der_3 (de, en, ru), plus the small fastText runtime
98.42%der_3 top-1 on the first 20 code points of FLORES sentences
130.4 kBpack file for all 109 CLD3 labels (full), plus its WebAssembly runtime
0inference servers: it runs where your code runs
Live demo

Identify a language as you type

Pick a model: this tab downloads it once from Hugging Face (a pinned revision) and runs it locally. Your text never leaves the browser.

Results update while you type. Short snippets are harder than full sentences.

Samples

File: der_3.dlid on Hugging Face (opens in a new tab)

Loading the runtime...

Model file
78.0 kB
Labels
pending
Loaded in
pending
Inference
pending

Detected language

    Scores are the models' native scores, not calibrated probabilities (fastText scores can slightly exceed 1). fastText models show their top 3; CLD3 returns only its top language with its own reliable flag. A pack chooses among its own languages and never answers "none of these": Ukrainian text in der_3 comes out as Russian.

    Language packs

    Pick the smallest pack that covers your languages

    Each pack is one file, shipped in the npm package as defuss-lid/packs/<name> and published on Hugging Face. What your users download is the pack plus its runtime: fastText needs only a small JavaScript runtime, while CLD3 also loads a WebAssembly core, so its runtime is much larger. Each card adds both up and compares the pack with the same languages on the other runtime.

    der_3

    fastText

    German, English and Russian. The smallest recommended pack, and the most accurate on short text in its set.

    78.0 kB pack, 77.5 kB all-in gzipped

    Figureder_3CLD3 alternative
    All-in, gzipped77.5 kB (better)199.4 kB
    Pack78.0 kB (better)106.6 kB
    Runtime17.3 kB (better)173.6 kB
    All-in95.4 kB (better)280.2 kB
    FLORES sentences100.00% (better)99.97%
    First 20 code points98.42% (better)94.99%
    MASSIVE utterances98.54% (better)97.19%

    3 languages · native PQ + int8

    • de
    • en
    • ru

    deferi_6

    fastText

    Six major European languages: German, English, French, Spanish, Russian and Italian. A larger file than its CLD3 counterpart, yet more accurate, and with the smaller runtime smaller all-in.

    127.5 kB pack, 119.8 kB all-in gzipped

    Figuredeferi_6CLD3 alternative
    All-in, gzipped119.8 kB (better)199.9 kB
    Pack127.5 kB107.3 kB (better)
    Runtime17.3 kB (better)173.6 kB
    All-in144.8 kB (better)280.9 kB
    FLORES sentences99.97% (better)99.93%
    First 20 code points93.74% (better)89.25%
    MASSIVE utterances96.92% (better)94.84%

    6 languages · native PQ + int8

    • de
    • en
    • fr
    • es
    • ru
    • it

    major_20

    CLD3

    Twenty widely used languages across Latin, Cyrillic, Arabic, Devanagari, Bengali, Thai, Hangul and CJK scripts. A close call: fastText wins on FLORES and uncompressed size, CLD3 on MASSIVE and gzipped size.

    110.4 kB pack, 202.6 kB all-in gzipped

    Figuremajor_20fastText alternative
    All-in, gzipped202.6 kB (better)214.1 kB
    Pack110.4 kB (better)241.8 kB
    Runtime173.6 kB17.3 kB (better)
    All-in284.0 kB259.1 kB (better)
    FLORES sentences99.65%99.70% (better)
    First 20 code points91.39%92.82% (better)
    MASSIVE utterances95.03% (better)91.94%

    20 languages · PQ + int8

    • en
    • de
    • fr
    • it
    • ru
    • id
    • es
    • pt
    • nl
    • ur
    • vi
    • th
    • zh
    • ja
    • pl
    • tr
    • ar
    • ko
    • bn
    • hi

    full

    CLD3

    Every CLD3 output label, including romanized variants such as ru-Latn and ja-Latn: for open-ended input. Smaller and more accurate than the compressed full fastText model, even with its larger runtime.

    130.4 kB pack, 218.7 kB all-in gzipped

    FigurefullfastText alternative
    All-in, gzipped218.7 kB (better)581.1 kB
    Pack130.4 kB (better)708.1 kB
    Runtime173.6 kB17.3 kB (better)
    All-in304.0 kB (better)725.4 kB
    FLORES sentences97.77% (better)93.14%
    First 20 code points78.80% (better)73.61%
    MASSIVE utterances88.60% (better)87.17%

    109 labels · PQ + int8

    Show all 109 labels
    • af
    • am
    • ar
    • az
    • be
    • bg
    • bg-Latn
    • bn
    • bs
    • ca
    • ceb
    • co
    • cs
    • cy
    • da
    • de
    • el
    • el-Latn
    • en
    • eo
    • es
    • et
    • eu
    • fa
    • fi
    • fr
    • fy
    • ga
    • gd
    • gl
    • gu
    • ha
    • haw
    • he
    • hi
    • hi-Latn
    • hmn
    • hr
    • ht
    • hu
    • hy
    • id
    • ig
    • is
    • it
    • ja
    • ja-Latn
    • jv
    • ka
    • kk
    • km
    • kn
    • ko
    • ku
    • ky
    • la
    • lb
    • lo
    • lt
    • lv
    • mg
    • mi
    • mk
    • ml
    • mn
    • mr
    • ms
    • mt
    • my
    • ne
    • nl
    • no
    • ny
    • pa
    • pl
    • ps
    • pt
    • ro
    • ru
    • ru-Latn
    • sd
    • si
    • sk
    • sl
    • sm
    • sn
    • so
    • sq
    • sr
    • st
    • su
    • sv
    • sw
    • ta
    • te
    • tg
    • th
    • tl
    • tr
    • uk
    • ur
    • uz
    • vi
    • xh
    • yi
    • yo
    • zh
    • zh-Latn
    • zu

    kB = 1,000 bytes. All-in counts each runtime once: a second pack on the same runtime adds only its own bytes. All-in, gzipped is what a page downloads from a server that compresses JavaScript. Accuracy is top-1 on held-out FLORES devtest sentences, their first 20 code points and MASSIVE test utterances. In each pair the better value is the darker one. The README size table lists every runtime file.

    How it works

    Two pretrained classifiers, one small file each

    No model was trained from scratch. defuss-lid reuses the released fastText lid.176 and Google CLD3 weights, keeps what a set of languages needs, and quantizes the rest.

    fastText path

    Pure JavaScript, no WebAssembly: typed arrays and arithmetic.

    1. TokenizeSplit on ASCII whitespace and append the end-of-sentence token </s>. No case folding or Unicode normalization, exactly as the released model expects.
    2. Hash n-gramsCharacter 2- to 4-grams with < > word boundaries go through fastText's signed-byte FNV-1a hash into 2,000,000 buckets; a binary search finds the rows the pack kept.
    3. Look up PQ embeddingsEvery word and n-gram row is a product-quantized code; its 16-dimensional vector is rebuilt from shared codebooks, and all vectors are averaged.
    4. Int8 classifierThe average meets int8 classifier rows, each with its own FP32 scale.
    5. Hierarchical softmaxA binary tree of sigmoid decisions turns those into a score per language; a pack keeps only the decisions on the paths to its languages.

    CLD3 path

    Google's original network and feature extraction, in WebAssembly.

    1. Fetch the packA .cld3q file holds the output columns of its languages, product-quantized embeddings and int8 dense layers.
    2. Verify the shared corecld3-core.bin.gz is the CLD3 WebAssembly with its weights cut out (85.6 kB). The runtime checks the SHA-256 of the download and of the unpacked core before using it.
    3. Reconstruct the weightsPQ codes expand, int8 layers are dequantized, and every left-out output column gets a bias of −1e9; the tensors go back into the pinned 744,144-byte WebAssembly layout.
    4. Native inferenceCLD3's own feature extraction and network run unchanged and return the top language with a reliable flag.

    Why the packs are small

    Output subsetting

    A pack keeps only what its languages need. For fastText these are the hierarchical-softmax decisions on the paths to the chosen labels, so each kept language keeps its original score. For CLD3 they are the chosen softmax columns.

    PQ + int8

    fastText packs reuse the released 8-group norm product quantizer unchanged and store classifier rows as int8. CLD3 packs product-quantize the three large embedding tables (4 groups × 256 centroids) and store dense layers as int8 with one scale per output neuron.

    Feature pruning

    fastText subsets keep only the word and n-gram rows seen in 100 FLORES dev sentences per language. Accuracy is measured on separate FLORES devtest and MASSIVE test data; the pruning API rejects test and devtest records.

    Runtime once

    Pack sizes exclude the runtime. Every CLD3 pack shares one core, so a second CLD3 pack costs only its own bytes.

    Honest limits

    Size is not RAM

    CLD3 packs compress storage and transfer only. They rebuild the original WebAssembly with its dense kernels, so inference memory does not shrink.

    Pruning costs coverage

    Chinese is the largest observed failure: the pruned 20-language fastText pack falls from 88.70%/85.24% to 60.49%/44.92% on MASSIVE zh-CN/zh-TW. The recommended major_20 is CLD3, which keeps its shared feature tables.

    Short text is harder

    der_3 gets 100.00% of FLORES sentences right but 98.42% of their first 20 code points; for full the drop is 97.77% to 78.80%.

    Scores are not probabilities

    An allowlisted pack picks among its languages and does not reject others. To abstain, set a threshold you have measured on your own data.

    Measured

    Held-out top-1 accuracy, pack by pack

    From the v0.3 report, measured on 2026-11-02: FLORES devtest sentences, their first 20 code points, and MASSIVE test utterances. Each set is its own closed-set task, so numbers are comparable within a set, not across sets. kB = 1,000 bytes.

    The four shipped packs, v0.3. All-in adds the runtime the pack needs. Scroll the table sideways for all columns.

    Pack Languages Method Pack kB All-in kB FLORES sentences % FLORES first 20 code points % MASSIVE utterances %
    der_33fastText native PQ + int878.095.4100.0098.4298.54
    deferi_66fastText native PQ + int8127.5144.899.9793.7496.92
    major_2020CLD3 PQ + int8110.4284.099.6591.3995.03
    full109CLD3 PQ + int8130.4304.097.7778.8088.60

    Full means the full native label inventory, not full benchmark coverage: FLORES evaluates 78 common labels and MASSIVE 51. Small differences, such as 99.97 against 99.93 on FLORES sentences for deferi_6 and its CLD3 alternative, are not a robust quality advantage; the report gives paired bootstrap intervals. The report also lists the configurations that are not shipped, among them the unmodified fastText lid.176 model: 93.11% (FLORES) and 87.17% (MASSIVE) on the full set.

    Read the full v0.3 report on GitHub (opens in a new tab)

    Unit test coverage

    96.3%
    Lines
    94.3%
    Branches
    93.8%
    Functions
    95.1%
    Statements

    45 unit tests of src/ run on Node.js and on Bun; V8 measures the coverage on Node.js, and the build fails below 90 % of lines, functions and statements or 80 % of branches.

    Use it

    Install once, identify anywhere JavaScript runs

    Every example on this page is executed by the project's end-to-end tests; the comment after each console.log is the exact output.

    Install

    The package carries both runtimes and the four recommended packs. No runtime dependencies, no native addons.

    npm install defuss-lid
    bun add defuss-lid

    Load a pack and predict

    Node.js 20.6 or newer resolves the node export condition, which reads the pack from the installed package. Run it with node detect.mjs.

    detect.mjs
    import {load} from 'defuss-lid/packs/der_3';
    
    const lid = await load();
    console.log(lid.predict('Guten Morgen!')[0].language); // de
    
    const top3 = lid.predict('Good morning, everyone!', {k: 3});
    console.log(top3.map(p => p.language).join(',')); // en,de,ru

    CLD3 packs

    CLD3 packs run in WebAssembly and return only their top language, with CLD3's reliable flag. Call dispose() when you are done.

    cld3.mjs
    import {load} from 'defuss-lid/packs/full';
    
    const lid = await load();
    const [top] = lid.predict('Gallia est omnis divisa in partes tres.');
    console.log(top.language); // la
    console.log(top.reliable); // true
    console.log(lid.predict('Guten Morgen!')[0].reliable); // false
    lid.dispose();

    Restrict the languages

    fastText models can rank only the languages your app supports. A restriction still picks one of them, so add a threshold to abstain.

    restrict.mjs
    import {load} from 'defuss-lid/packs/deferi_6';
    
    const lid = await load();
    const allowed = {languages: ['de', 'en', 'fr']};
    console.log(lid.predict('Guten Tag!', allowed)[0].language); // de
    console.log(lid.predict('Buongiorno a tutti!', allowed)[0].language); // de
    console.log(lid.predict('Buongiorno a tutti!', {...allowed, threshold: 0.5}).length); // 0

    API at a glance

    CallReturns
    load(options?)from defuss-lid/packs/<name>The pack, loaded: a fastText model or a CLD3 pack
    loadModel(url)from defuss-lidA fastText LidModel from any .dlid URL
    model.predict(text, {k, threshold, languages})fastText[{language, score}], best first; [] for blank text
    loadCld3Pack(url, {coreURL})from defuss-lid/cld3-pack{predict, dispose, meta} for any .cld3q URL
    pack.predict(text, {threshold})CLD3[{language, score, reliable}], top-1 only
    PACKSfrom defuss-lid/packsNames, languages, bytes and SHA-256 of the four packs

    The models are on Hugging Face

    Download the packs directly, or pin the exact revision this page uses: 24d27e5. The npm package ships byte-identical copies.