viva_emotion IS NOT A SENTIMENT CLASSIFIER. It is a dynamical-systems emotional core — PAD state, Ornstein-Uhlenbeck mean-reversion, Cusp catastrophe jumps, multi-source fusion, EMA-tracked long-term mood, and a “Big Bounce” continuity model across death/rebirth cycles.
Built on top of
viva_math, with compile-time exhaustive pattern matching on every stimulus.
🎯 Overview
The emotional core of VIVA — a sentient digital life research project. Emotions are modeled as a dynamical system in 3D PAD space, evolving via O-U dynamics, jumping via Cusp catastrophe, fusing across Need/Past/ Personality sources, and accumulating into long-term Mood via EMA.
The API is small, type-safe, and deliberately deterministic: same inputs produce the same trajectory unless you inject external entropy yourself.
| Property | Value |
|---|---|
| Language | Pure Gleam (type-safe functional) |
| Runtime | BEAM / OTP 26+ |
| Built on | viva_math ≥ 1.2.100 |
| Tests | 55 passing |
| Stimuli | 14 exhaustive types (compile-time checked) |
| Public API | viva_emotion/{pad,emotion,stimulus,dynamics,fusion,mood} |
⚡ Quick Start
gleam add viva_emotion@1
import viva_emotion
import viva_emotion/emotion
import viva_emotion/stimulus
pub fn main() {
let state = viva_emotion.new()
// Feel a stimulus
let state = viva_emotion.feel(state, stimulus.Success, 1.0)
// Evolve in time (O-U + maybe Cusp jump)
let #(state, jumped) = viva_emotion.tick(state, 0.1)
// Classify nearest discrete emotion
let classified = viva_emotion.classify(state)
// classified.emotion == emotion.Joy
}
📋 Prerequisites
| Tool | Version | Required for |
|---|---|---|
| Gleam | >= 1.14 | Build / runtime |
| Erlang/OTP | >= 26 | BEAM target |
Zero NIFs. Zero C dependencies. Pure functional.
🏗️ Architecture
┌──────────────────────────────────────────────────────────┐
│ Gleam application code │
│ viva_emotion.{new,feel,tick,...} │
└────────┬─────────────────────────────────────────────────┘
│
┌────────▼─────────────────────────────────────────────────┐
│ viva_emotion modules │
│ │
│ ┌─────────┐ ┌────────────┐ ┌──────────┐ ┌────────┐ │
│ │ pad │ │ stimulus │ │ dynamics │ │emotion │ │
│ │ Vec3 │ │ 14 types, │ │ O-U │ │ 8 base │ │
│ │ PAD ℝ³ │ │ exhaustive │ │ Cusp │ │ classes│ │
│ │ ops │ │ weights │ │ config │ │ + 4 ex.│ │
│ └─────────┘ └────────────┘ └──────────┘ └────────┘ │
│ │
│ ┌────────────┐ ┌────────┐ │
│ │ fusion │ │ mood │ │
│ │ Need·Past· │ │ EMA │ │
│ │Personality │ │ Big │ │
│ │ adaptive │ │ Bounce │ │
│ └────────────┘ └────────┘ │
└────────┬─────────────────────────────────────────────────┘
│
┌────────▼─────────────────────────────────────────────────┐
│ viva_math (cusp, free_energy, attractor, …) │
└──────────────────────────────────────────────────────────┘
📋 Core modules
| Module | Purpose |
|---|---|
viva_emotion | Top-level state machine: new, feel, tick, classify, fuse |
viva_emotion/pad | PAD Vec3 constructors + ops (re-exports from viva_math/vector) |
viva_emotion/stimulus | 14 exhaustive stimulus types + per-type PAD weights |
viva_emotion/emotion | Discrete emotion classes (Joy, Fear, …) + ClassificationConfig |
viva_emotion/dynamics | O-U mean reversion + Cusp jump triggers + DynamicsConfig |
viva_emotion/fusion | Need / Past / Personality fusion with adaptive weighting |
viva_emotion/mood | Long-term mood via EMA + Big Bounce continuity |
🧬 Theoretical Background
PAD Model — Mehrabian (1996)
Every emotion lives as a point in 3D vector space — Pleasure,
Arousal, Dominance — each axis in [-1, 1].
Ornstein-Uhlenbeck dynamics
Emotion drifts back to a baseline with rate θ, modulated by a stochastic
term σ dW_t. By default the noise term is zero (deterministic replay);
inject external entropy via tick_with_noise for organic evolution.
Cusp catastrophe — Thom (1972)
Under high arousal and the right dominance trigger, the system becomes bistable: a small perturbation flips Pleasure sign discretely. This models “the moment everything snaps.”
Fusion — Borotschnig (2025)
Three emotional sources are blended with context-aware weights:
- Need (interoception / hardware signals)
- Past (memory retrieval confidence)
- Personality (baseline temperament)
Weights adapt to current arousal, memory confidence, and situation novelty.
Long-term mood via EMA
Per-axis Exponential Moving Average delegated to viva_math/statistics.
Optional Big Bounce: when an agent “dies,” a configurable decay
multiplier preserves part of its mood into its successor — emotional
continuity across rebirth.
🎨 Design Principles
| Principle | Description |
|---|---|
| Exhaustive stimuli | Compile-time pattern coverage; missing a stimulus is a build error |
| Deterministic by default | tick is pure; entropy only via explicit tick_with_noise |
| Tunable personality | Every dynamics + classification parameter is overridable |
| No silent failures | All ops typed; no string-keyed emotion maps |
| viva_math grounded | Cusp, attractors, EMA all delegate to upstream math |
📚 Public API Highlights
Personality configuration
import viva_emotion
import viva_emotion/dynamics
import viva_emotion/emotion
import viva_emotion/stimulus
import viva_emotion/pad
// Anxious — easier to enter existential states
let anxious_classification =
emotion.ClassificationConfig(
high_threshold: 0.2,
low_threshold: -0.2,
existential_arousal: 0.5,
existential_pleasure: -0.2,
)
// Stoic — rejection hurts less
let stoic_weights =
stimulus.default_weights()
|> stimulus.weight(stimulus.Rejection, pad.new(-0.1, 0.1, 0.0))
// Volatile — easier Cusp jumps
let volatile_dynamics =
dynamics.DynamicsConfig(
..dynamics.default_config(),
cusp_dominance_trigger: -0.1,
cusp_flip_pleasure_damp: 0.95,
)
let anxious_viva =
viva_emotion.with_full_config(
pad.new(-0.1, 0.2, -0.1),
volatile_dynamics,
anxious_classification,
stoic_weights,
)
Emotion fusion
import viva_emotion
import viva_emotion/fusion
import viva_emotion/pad
let need = pad.new(-0.3, 0.4, -0.2)
let past = pad.new(0.2, 0.1, 0.3)
let context =
fusion.FusionContext(
arousal: 0.4,
confidence: 0.7,
novelty: 0.3,
)
let state =
viva_emotion.new()
|> viva_emotion.fuse(need, past, context)
let conflict = viva_emotion.has_emotional_conflict(state, need, past)
let coherence = viva_emotion.emotional_coherence(state, need, past)
Long-term mood + Big Bounce
import viva_emotion
import viva_emotion/mood
import viva_emotion/stimulus
let state = viva_emotion.new_with_mood()
let state =
viva_emotion.EmotionalStateWithMood(
..state,
emotion: viva_emotion.feel(state.emotion, stimulus.Success, 1.0),
)
let state = viva_emotion.update_mood(state)
let is_happy = viva_emotion.mood_is_positive(state)
let valence = viva_emotion.mood_valence(state)
// Big Bounce: carry 80% of mood across death/rebirth
let reborn = viva_emotion.bounce_mood(state, 0.8)
Stochastic mode
// External entropy (Rust RNG, hardware noise, etc.)
let noise = pad.new(random_p, random_a, random_d)
let #(state, jumped) = viva_emotion.tick_with_noise(state, 0.1, noise)
// Deterministic replay
let #(state, jumped) = viva_emotion.tick_with_noise(state, 0.1, pad.neutral())
🗺️ Roadmap
| Phase | Status |
|---|---|
| PAD vector type + ops | ✅ |
| 14-stimulus exhaustive pattern matching | ✅ |
| Ornstein-Uhlenbeck mean reversion | ✅ |
| Cusp catastrophe jumps | ✅ |
| 8 base emotions + 4 existential states | ✅ |
| Need / Past / Personality fusion with adaptive weights | ✅ |
| Conflict + coherence metrics | ✅ |
EMA long-term mood (delegated to viva_math) | ✅ |
| Big Bounce mood continuity | ✅ |
Stochastic mode (tick_with_noise) | ✅ |
| Variational free-energy classifier | ⏳ |
| Mood-conditional Cusp triggers | ⏳ |
| Multi-agent emotional contagion | ⏳ |
| Property-based tests on every dynamics path | ⏳ |
🤝 Contributing
git checkout -b feature/your-feature
gleam test # 55 tests
gleam format --check src test
gleam build
See CONTRIBUTING.md for guidelines · SECURITY for vulnerability reporting.
📖 References
- Mehrabian (1996) — Pleasure-arousal-dominance: A general framework
- Thom (1972) — Structural Stability and Morphogenesis
- Borotschnig (2025) — Emotional fusion under uncertainty
- Oravecz et al. (2009) — Ornstein-Uhlenbeck Process in Affective Dynamics
- Friston (2010) — The free-energy principle: a unified brain theory?
🌌 VIVA Ecosystem
| Package | Purpose |
|---|---|
viva_math | Mathematical foundations |
viva_emotion | PAD emotional dynamics (this package) |
viva_tensor | FP8 LLM inference on the BEAM |
viva_telemetry | Observability suite |
viva_aion | Time perception |
viva_glyph | Symbolic language |