Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

36 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Gleam BEAM OTP Hex PAD Tests Version License


"Joy is a vector. Fear is a fold. Mood is the average of the soul."


Important

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

Star if BEAM should know how it feels ⭐

GitHub stars

Created by Gabriel Maia Β· MIT License

About

Type-safe emotional core for digital consciousness. PAD model, O-U dynamics, Cusp catastrophe - all in pure Gleam.

Topics

Resources

Security policy

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages