Table of Contents

Detect Emotions in Text

Emotion detection goes beyond positive/negative sentiment. It classifies text into specific emotional categories: happiness, anger, sadness, fear, and neutral. This gives applications a deeper understanding of user intent and tone. LM-Kit.NET's EmotionDetection class provides this capability with confidence scoring, neutral support, and cancellation token support, all running locally.


Why Emotion Detection Matters

Two enterprise problems that on-device emotion detection solves:

  1. Customer support triage. Routing angry customers to senior agents vs happy ones to upsell flows requires understanding the specific emotion, not just polarity. Emotion detection lets you build routing rules that match the customer's actual state to the right response team.
  2. Employee engagement monitoring. Detecting fear or sadness patterns in internal surveys without sending data to the cloud keeps sensitive HR data on-premises. Local emotion analysis lets you surface trends across teams without creating privacy risks.

Prerequisites

Requirement Minimum
.NET SDK 8.0+
VRAM 2+ GB
Disk ~2 GB free for model download

Step 1: Create the Project

dotnet new console -n EmotionQuickstart
cd EmotionQuickstart
dotnet add package LM-Kit.NET

Step 2: Basic Emotion Detection

using System.Text;
using LMKit.Model;
using LMKit.TextAnalysis;

LMKit.Licensing.LicenseManager.SetLicenseKey("");

Console.InputEncoding = Encoding.UTF8;
Console.OutputEncoding = Encoding.UTF8;

// ──────────────────────────────────────
// 1. Load model
// ──────────────────────────────────────
Console.WriteLine("Loading model...");
using LM model = LM.LoadFromModelID("gemma3:1b",
    downloadingProgress: (_, len, read) =>
    {
        if (len.HasValue) Console.Write($"\r  Downloading: {(double)read / len.Value * 100:F1}%   ");
        return true;
    },
    loadingProgress: p => { Console.Write($"\r  Loading: {p * 100:F0}%   "); return true; });
Console.WriteLine("\n");

// ──────────────────────────────────────
// 2. Create emotion detector
// ──────────────────────────────────────
var detector = new EmotionDetection(model)
{
    NeutralSupport = true  // enable neutral category
};

// ──────────────────────────────────────
// 3. Analyze sample texts
// ──────────────────────────────────────
string[] texts =
{
    "I'm thrilled with the new features in this release!",
    "This is completely unacceptable. I want a refund immediately.",
    "I lost my job today and I don't know what to do.",
    "What if the system crashes during the demo tomorrow?",
    "The package arrived on Tuesday as expected."
};

Console.WriteLine("Emotion Detection Results:\n");

foreach (string text in texts)
{
    EmotionDetection.EmotionCategory emotion = detector.GetEmotionCategory(text);
    float confidence = detector.Confidence;

    Console.WriteLine($"  Text: \"{text}\"");
    Console.WriteLine($"  Emotion: {emotion}  (confidence: {confidence:F2})\n");
}

Expected output:

  Text: "I'm thrilled with the new features in this release!"
  Emotion: Happiness  (confidence: 0.95)

  Text: "This is completely unacceptable. I want a refund immediately."
  Emotion: Anger  (confidence: 0.92)

  Text: "I lost my job today and I don't know what to do."
  Emotion: Sadness  (confidence: 0.89)

  Text: "What if the system crashes during the demo tomorrow?"
  Emotion: Fear  (confidence: 0.84)

  Text: "The package arrived on Tuesday as expected."
  Emotion: Neutral  (confidence: 0.91)

Step 3: Batch Processing with Async

Process a file of customer reviews asynchronously and generate a summary report:

using System.Text;
using LMKit.Model;
using LMKit.TextAnalysis;

LMKit.Licensing.LicenseManager.SetLicenseKey("");

Console.InputEncoding = Encoding.UTF8;
Console.OutputEncoding = Encoding.UTF8;

// ──────────────────────────────────────
// 1. Load model
// ──────────────────────────────────────
Console.WriteLine("Loading model...");
using LM model = LM.LoadFromModelID("gemma3:1b",
    downloadingProgress: (_, len, read) =>
    {
        if (len.HasValue) Console.Write($"\r  Downloading: {(double)read / len.Value * 100:F1}%   ");
        return true;
    },
    loadingProgress: p => { Console.Write($"\r  Loading: {p * 100:F0}%   "); return true; });
Console.WriteLine("\n");

// ──────────────────────────────────────
// 2. Create emotion detector
// ──────────────────────────────────────
var detector = new EmotionDetection(model)
{
    NeutralSupport = true  // enable neutral category
};

// Process a batch of customer reviews asynchronously
string[] reviews = File.ReadAllLines("reviews.txt");

var results = new List<(string Text, EmotionDetection.EmotionCategory Emotion, float Confidence)>();

foreach (string review in reviews)
{
    if (string.IsNullOrWhiteSpace(review)) continue;

    var emotion = await detector.GetEmotionCategoryAsync(review);
    results.Add((review, emotion, detector.Confidence));
}

// Group by emotion category
var grouped = results.GroupBy(r => r.Emotion);

Console.WriteLine("── Emotion Summary ──\n");
foreach (var group in grouped.OrderByDescending(g => g.Count()))
{
    Console.WriteLine($"  {group.Key,-12} {group.Count(),4} reviews  (avg confidence: {group.Average(r => r.Confidence):F2})");
}

Step 4: Binary Emotion Mode (Without Neutral)

When you need every input classified into a specific emotion, disable NeutralSupport to force the model to pick one of the four core categories:

using System.Text;
using LMKit.Model;
using LMKit.TextAnalysis;

LMKit.Licensing.LicenseManager.SetLicenseKey("");

Console.InputEncoding = Encoding.UTF8;
Console.OutputEncoding = Encoding.UTF8;

// ──────────────────────────────────────
// 1. Load model
// ──────────────────────────────────────
Console.WriteLine("Loading model...");
using LM model = LM.LoadFromModelID("gemma3:1b",
    downloadingProgress: (_, len, read) =>
    {
        if (len.HasValue) Console.Write($"\r  Downloading: {(double)read / len.Value * 100:F1}%   ");
        return true;
    },
    loadingProgress: p => { Console.Write($"\r  Loading: {p * 100:F0}%   "); return true; });
Console.WriteLine("\n");

var binaryDetector = new EmotionDetection(model)
{
    NeutralSupport = false  // only: Happiness, Anger, Sadness, Fear
};

This is useful when ambiguous inputs should still receive a definitive classification. The model will select the closest matching emotion even for neutral-sounding text.


Step 5: Emotion-Based Routing

Use detected emotions to route customer messages to the appropriate support queue:

using System.Text;
using LMKit.Model;
using LMKit.TextAnalysis;

LMKit.Licensing.LicenseManager.SetLicenseKey("");

Console.InputEncoding = Encoding.UTF8;
Console.OutputEncoding = Encoding.UTF8;

// ──────────────────────────────────────
// 1. Load model
// ──────────────────────────────────────
Console.WriteLine("Loading model...");
using LM model = LM.LoadFromModelID("gemma3:1b",
    downloadingProgress: (_, len, read) =>
    {
        if (len.HasValue) Console.Write($"\r  Downloading: {(double)read / len.Value * 100:F1}%   ");
        return true;
    },
    loadingProgress: p => { Console.Write($"\r  Loading: {p * 100:F0}%   "); return true; });
Console.WriteLine("\n");

// ──────────────────────────────────────
// 2. Create emotion detector
// ──────────────────────────────────────
var detector = new EmotionDetection(model)
{
    NeutralSupport = true  // enable neutral category
};

EmotionDetection.EmotionCategory emotion = detector.GetEmotionCategory(customerMessage);

string queue = emotion switch
{
    EmotionDetection.EmotionCategory.Anger => "priority-escalation",
    EmotionDetection.EmotionCategory.Fear => "reassurance-team",
    EmotionDetection.EmotionCategory.Sadness => "empathy-response",
    EmotionDetection.EmotionCategory.Happiness => "upsell-opportunity",
    _ => "general-queue"
};

Console.WriteLine($"Routing to: {queue}");

Step 6: Interactive Detector

using System.Text;
using LMKit.Model;
using LMKit.TextAnalysis;

LMKit.Licensing.LicenseManager.SetLicenseKey("");

Console.InputEncoding = Encoding.UTF8;
Console.OutputEncoding = Encoding.UTF8;

// ──────────────────────────────────────
// 1. Load model
// ──────────────────────────────────────
Console.WriteLine("Loading model...");
using LM model = LM.LoadFromModelID("gemma3:1b",
    downloadingProgress: (_, len, read) =>
    {
        if (len.HasValue) Console.Write($"\r  Downloading: {(double)read / len.Value * 100:F1}%   ");
        return true;
    },
    loadingProgress: p => { Console.Write($"\r  Loading: {p * 100:F0}%   "); return true; });
Console.WriteLine("\n");

// ──────────────────────────────────────
// 2. Create emotion detector
// ──────────────────────────────────────
var detector = new EmotionDetection(model)
{
    NeutralSupport = true  // enable neutral category
};

Console.WriteLine("Enter text to detect emotion (or 'quit' to exit):\n");

while (true)
{
    Console.ForegroundColor = ConsoleColor.Green;
    Console.Write("Text: ");
    Console.ResetColor();

    string? input = Console.ReadLine();
    if (string.IsNullOrWhiteSpace(input) || input.Equals("quit", StringComparison.OrdinalIgnoreCase))
        break;

    EmotionDetection.EmotionCategory emotion = detector.GetEmotionCategory(input);
    float confidence = detector.Confidence;

    Console.ForegroundColor = emotion switch
    {
        EmotionDetection.EmotionCategory.Happiness => ConsoleColor.Green,
        EmotionDetection.EmotionCategory.Anger => ConsoleColor.Red,
        EmotionDetection.EmotionCategory.Sadness => ConsoleColor.Blue,
        EmotionDetection.EmotionCategory.Fear => ConsoleColor.Yellow,
        _ => ConsoleColor.Gray
    };

    Console.Write($"  {emotion}");
    Console.ResetColor();
    Console.WriteLine($" (confidence: {confidence:P0})\n");
}

Emotion Categories Reference

Value Category Description
0 Neutral No strong emotion detected (requires NeutralSupport = true)
1 Happiness Joy, excitement, satisfaction, gratitude
2 Anger Frustration, outrage, irritation
3 Sadness Disappointment, grief, loss
4 Fear Worry, anxiety, concern, apprehension

Common Issues

Problem Cause Fix
All results are Neutral NeutralSupport enabled with ambiguous text Set NeutralSupport = false for forced classification
Low confidence scores Short or ambiguous input text Provide more context in the input text
Wrong emotion detected Model too small for nuanced emotion Use a larger model (gemma3:4b or qwen3:4b)

Next Steps

Share