Preview of the facial-animation library for a 0.96″ 128×64 OLED. The frame is computed by the js/robot-face.js module, the twin of the C component that draws the same face on the device. Eye shape is set by two numbers, valence and arousal; emotion traits, overlays and motion are layered on top.
Source code on GitLabREADMEMIT license · © 2026 MaksV
Every activity and every emotion on its own screen, each with its own seed. Click a screen to show that face on the large module.
Each emotion is a point on the V–A plane plus its own recognizable trait: eye shape, overlay or motion. An LLM emits an emoji and the server maps it to an emotion name. Emotions tagged rude are for a rude character only: the server leaves them out of a friendly robot's emoji list.
The robot switches states itself on protocol messages; the trigger message is shown under each button. Listening, Thinking and Error temporarily replace the emotion with their own face, Speaking adds a mouth over the emotion, Idle brings the emotion back.
An activity shows only in Idle: its own face, prop and gaze. Listening, Thinking, Speaking and Error hide it; “No activity” brings the emotion back. On a device the activities can be chosen by a server on a schedule.
Drag the point. Horizontal is valence, vertical is arousal. The face is a bilinear blend of four corner faces: P(V, A) = P₀ + V·EV + A·EA + V·A·EVA. Angry and sad lids, heavy sleepy lids and the smiling eyes are enabled through max(0, …), only in their own quadrant.
The emotion arrives in the emotion field together with a sentence. The device attaches it as a marker in the audio queue, and the face changes when that sentence starts playing.
Example schedule a server could run (accelerated). After a conversation: 2 min of the base face. By day an activity runs 1–5 min, then 1–3 min of the base face; the next activity is chosen at random by weight and never repeats the previous one. At night the robot sleeps with a dimmed screen; in the morning it returns to the base face and the daytime schedule.
Current eye targets. All model numbers live in presets/face.json, from which both the firmware and this module are built.