Arvolve Labs, for robot and AI device builders

A robot face that performs, not one that switches moods

Your robot's language model can already say what it feels. This engine makes its face show it, with the timing film animators use: a wind-up before a reaction, an overshoot that settles, blinks on changes of thought, an eye dart before it speaks.

Same script, same poses. Left: the engine. Right: the usual approach, a quick tween between moods and a blink timer, given exactly the same tags. Tick slow motion to see the wind-up and the settle. This demo runs the real engine with 16 of its 65 expressions. For round screens, OLEDs, LED rings and servo heads; a one-off download with JavaScript and Python, an editor, an ESP32 receiver and a skill file for your coding agent.

What it does that a mood swap does not

Anticipation

Before a big reaction the face moves the other way for 60 to 140 ms. Surprise starts with a squeeze; anger winds up before it lands.

Overshoot and settle

Every region rides a spring tuned per expression. Delight pops past its mark and settles; sadness arrives slowly and never bounces.

Overlap

Eyes move first, then lids, brows, mouth, and the head last, so a change reads as one thought travelling through the face.

Half-expressions

One side leads by 35 to 65 ms and settles a little further. Perfect symmetry is what makes machines look dead.

Blinks with meaning

Blinks land on changes of thought, big eye turns and state changes. It blinks less while listening, more while thinking, never mid-startle.

Gaze that thinks

Eyes go up and aside while it works something out, dart away just before it speaks, hold on you with small saccades while you talk.

Speech and listening

The mouth follows the voice, stressed syllables get a small beat, and when you pause after talking for a while it nods.

An idle that breathes

Breathing, drift and an occasional look around, so a robot waiting for its next prompt still looks alive.

Frame-by-frame comparison of neutral to surprised: the mood swap slides straight to the pose, the engine squeezes first, one side leads, then it springs past the mark and settles

Your agent writes the tags. The engine does the acting.

Your conversational model puts a tag before the words it belongs to. The engine handles everything between the tags: timing, transitions, blinks, gaze and the mouth.

65 expressions written in FACS, the action-unit system used for film facial rigs, from content and amused to skeptical, realizing and sheepish. Synonyms such as thrilled or sceptical resolve on their own.

States you set from code: listening while the person talks, thinking while the reply is generated, speaking when it arrives. say() tells you when to start the audio so the eye dart lands before the first word.

A prompt block for your agent teaches it the vocabulary and the restraint: one tag per sentence at most, quiet tags for everyday talk.

face.setState("thinking");      // while the LLM works

const reply = "[realizing] Oh, I know this one. " +
              "[happy 0.7] It's on the second shelf.";
const { start } = face.say(reply);
tts.speak(FaceEngine.stripTags(reply), start - face.t);

// every frame
face.setAudioLevel(tts.loudness(), "speech");
FaceRenderer.draw(ctx, face.update(dt), config);
// or: servoAngles(pose, servos), ledRing(pose, 16),
//     packFrame(pose) for an ESP32 over serial

One intent stream, any face hardware

Capsule-style glowing eyes and mouth on a wide visor screen, smiling
Visor or tablet screenCapsule, Iris or Minimal style, with a mouth
Two round displays showing stylised eyeballs looking up and aside while thinking
Two round screensGC9A01 240x240, ESP32 receiver included
A 128 by 64 monochrome OLED showing pixel eyes in a sad expression
OLED 128x64SSD1306 page buffer, 1-bit
A 16-LED ring lit in blue showing two eye arcs and a mouth arc
LED ring12, 16 or 24 WS2812 LEDs
Preview of a servo head with lids and brows next to a list of servo angles in degrees
Servo headPan, tilt, roll, lids, brows, jaw, antennas

How you wire it in

  1. Describe your face

    Open the editor, pick your hardware and a style, tune the performance, test it with scripts or your microphone, export face-config.json.

  2. Hand it to your coding agent

    SKILL.md tells Claude Code, Codex or Cursor how to wire the engine into a browser face, Node, Python, an ESP32, servos or an LED ring, and the traps to avoid.

  3. Teach your talking agent

    Paste INTENT_TAGS.md into its system prompt. From then on its replies carry the tags, and the face performs them.

The editor: hardware and style settings on the left, a live robot face in the centre with a log of animation decisions, expression buttons on the right

What is in the download

  • The engine in JavaScript (browser or Node) and Python, frame-identical, no dependencies.
  • 65 expressions as FACS action units, with timing classes, states and synonyms, all editable.
  • The editor: hardware, style, eight performance controls, live tests, side-by-side comparison, export.
  • Hardware adapters: canvas renderers, servo angles with limits, LED ring colours, SSD1306 buffer, a serial frame.
  • ESP32 receiver for two GC9A01 round screens, and a serial bridge.
  • Reachy Mini bridge for head and antennas (experimental).
  • Skill file and tag prompt for your coding agent and your conversational agent.
  • Tests that prove the timing rules and the Python parity.

Who it is for

  • Desk and home robot builders with an LLM that talks well and a face that stares.
  • AI toy and companion device studios who need a face with character without hiring an animator for every state.
  • Kiosk, avatar and assistant developers putting a face on a screen.
  • ESP32 and Raspberry Pi makers with round displays, OLEDs, LED rings or servos.

Honest limits

  • The ESP32 sketch and the Reachy Mini bridge have not been run on hardware yet. The frame format they use is tested.
  • Three face styles to start. The faces are drawn, not sculpted; bring your own art and drive it with the same channels.
  • The engine is code you can read. What you pay for is the tuned expression and timing data, the editor and the integration work done for you.
£29one-off

Use it in your own robots and products, any number of units. Chrome, Edge or Firefox for the editor; Node 18+ or Python 3.8+ to run it in your robot.

Questions

Free eye libraries exist. Why pay?

They do, and they are good at what they do: FluxGarage RoboEyes and esp32-eyes give you moods, blinks and gaze on small screens. This engine is the layer above: how a face moves between states, when it blinks and where it looks while a conversation happens, driven straight from your agent's words. If you only need a mood swap, use the free ones.

Does it need the internet or an account?

No. Everything runs on your device. Nothing is sent anywhere, including the microphone in the editor.

Which language model does it work with?

Any. The engine reads tags in the reply text, so Claude, GPT, Gemini or a local model all work. Paste the tag prompt into its system prompt.

Can I add my own expressions or face art?

Yes. Expressions are plain JSON in FACS units. Your own art can read the 20 channels (gaze, lids, brows, mouth, head, glow) the same way the built-in styles do.

What if it does not work for my robot?

Write to [email protected] with your hardware and what you saw.

Guides by hardware and question