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

sentiment-node

v1.0.0

Published

Lightweight sentiment analysis library for Node.js that predicts whether text is positive, negative, or neutral.

Readme

sentiment-node

npm version license node

Offline sentiment analysis for Node.js — classifies English text as positive, negative, or neutral using a TF-IDF vectorizer and an ONNX machine learning model. No API keys. No internet required.

import { init, analyze } from 'sentiment-node'

await init()

await analyze('I absolutely love this!')
// → { label: 'positive', confidence: 0.83, scores: { negative: 0.06, neutral: 0.11, positive: 0.83 } }

Installation

npm install sentiment-node

Getting Started

Follow these three steps to go from zero to running predictions.

Step 1 — Install dependencies

npm install

Step 2 — Import and initialize

Always call init() once before doing any analysis. It loads the vocabulary and model into memory.

import { init, analyze } from 'sentiment-node'

await init()
// ✅ Vocabulary loaded: 44,961 tokens
// ✅ ONNX model loaded
// 🚀 Ready!

Step 3 — Analyze text

const result = await analyze('The product quality exceeded my expectations.')
console.log(result)
// Output:
{
  label: 'positive',
  confidence: 0.8315,
  scores: {
    negative: 0.0617,
    neutral:  0.1068,
    positive: 0.8315
  }
}

That's it. See the full API below for batch analysis, thresholds, and statistics.


API

init(options?)

Loads the model files into memory. Call this once at startup before anything else.

// Default — loads from the built-in models/ folder
await init()

// Custom paths (optional)
await init({
  vocabPath: './path/to/tfidf_vocab.json',
  modelPath: './path/to/sentiment_model.onnx'
})

analyze(text, options?)

Analyzes a single string and returns its sentiment.

const result = await analyze('Shipping was fast and packaging was great!')

Return value:

{
  label: 'positive',     // the predicted sentiment
  confidence: 0.8315,    // how confident the model is (0 to 1)
  scores: {
    negative: 0.0617,    // raw probability for each class
    neutral:  0.1068,
    positive: 0.8315
  }
}

With threshold option — if confidence is too low, output is forced to neutral:

const result = await analyze('meh it was fine I guess', { threshold: 0.6 })
// confidence 0.45 < threshold 0.6 → label forced to 'neutral'

With detectLanguage option — skips non-English text safely:

const result = await analyze('Je suis content', { detectLanguage: true })
// Output:
{
  label: 'unknown',
  confidence: 0,
  scores: { negative: 0, neutral: 0, positive: 0 },
  language: { code: 'fra', isEnglish: false, supported: false },
  warning: "Language 'fra' is not supported. Model trained on English only."
}

analyzeBatch(texts[], options?)

Analyzes an array of strings in parallel. Returns all results plus timing metadata.

import { init, analyzeBatch } from 'sentiment-node'

await init()

const { results, meta } = await analyzeBatch([
  'Amazing product, highly recommend!',
  'Terrible quality, waste of money.',
  'It arrived on time.',
  'Not bad at all.',
  'I am so happy with this purchase!'
])

Return value:

{
  results: [
    { index: 0, text: 'Amazing product...', label: 'positive', confidence: 0.80, scores: { ... } },
    { index: 1, text: 'Terrible quality...', label: 'negative', confidence: 0.42, scores: { ... } },
    { index: 2, text: 'It arrived on time.', label: 'neutral',  confidence: 0.46, scores: { ... } },
    { index: 3, text: 'Not bad at all.',     label: 'negative', confidence: 0.54, scores: { ... } },
    { index: 4, text: 'I am so happy...',    label: 'positive', confidence: 0.68, scores: { ... } }
  ],
  meta: {
    total: 5,
    elapsed: '11ms',
    avg: '2.20ms per item'
  }
}

All analyze options (threshold, detectLanguage) are supported here too.


summary(texts[], options?)

Analyzes an array of strings and returns aggregate statistics — useful for dashboards, reviews, or feedback forms.

import { init, summary } from 'sentiment-node'

await init()

const stats = await summary([
  'Love it!',
  'Hate it!',
  'Its okay',
  'Great product',
  'Worst ever',
  'Not sure about this',
  'Would buy again',
  'Never buying again'
])

console.log(stats)

Return value:

{
  total: 8,
  counts: {
    positive: 4,
    neutral:  1,
    negative: 3
  },
  percentages: {
    positive: 50,
    neutral:  13,
    negative: 38
  },
  dominant: 'positive'   // the most common sentiment
}

Options Reference

All options are passed as the second argument to analyze, analyzeBatch, or summary.

| Option | Type | Default | Description | | ---------------- | --------- | ------- | ---------------------------------------------------------------------------- | | threshold | number | 0.0 | Minimum confidence to trust — anything below is returned as neutral | | detectLanguage | boolean | false | When true, non-English text returns { label: 'unknown', warning: '...' } |

Choosing a threshold:

| Threshold | Behaviour | | --------------- | ------------------------------------------ | | 0.0 (default) | Always returns the model's best guess | | 0.5 | Returns neutral when the model is unsure | | 0.7 | Only trusts high-confidence predictions |


How It Works

Input text
    │
    ▼
① Clean       lowercase, remove noise
    │
    ▼
② Vectorize   TF-IDF (sublinear_tf, bigrams, 44k vocab) → Float32Array
    │
    ▼
③ Infer       ONNX model → class label + probabilities
    │
    ▼
④ Threshold   confidence < threshold? → force neutral
    │
    ▼
Result object  { label, confidence, scores }

The model was trained with scikit-learn's TfidfVectorizer (settings: max_features=50000, ngram_range=(1,2), sublinear_tf=True) and exported to ONNX format. The JavaScript vectorizer replicates the exact same transform so predictions are consistent with the Python training environment.


Contributing

Contributions, bug reports, and suggestions are welcome! Please read CONTRIBUTING.md before opening a pull request.


Community

| Document | Description | | ---------------------------------------- | ----------------------------------------- | | CONTRIBUTING.md | How to contribute code, docs, or fixes | | CODE_OF_CONDUCT.md | Expected behaviour in project spaces | | SECURITY.md | How to responsibly report a vulnerability |


License

Apache-2.0 © Anas Alam