Skip to content
Core API/Expressions

ALLPERSONAS / DEVELOPERS

Expressions

Let the face respond to the moment.

Try an expression

Expressions are typed inputs. Pick a cue to see how it changes the face.

Live SDK@allpersonas/core
Ruby

MAKE IT YOURS

Ready when you are
Use this face in your app
Your selected face — assistant.ts
import { createFace } from '@allpersonas/core';

export function mountAssistant(host: HTMLElement) {
  Object.assign(host.style, { width: '280px', height: '340px' });
  const face = createFace(host, {
    avatar: "ruby",
    expression: "attentive",
  });
  // Update when your agent's state changes.
  // face.update({ thinking: true });
  return () => face.destroy();
}

Expression catalog

neutralattentivecuriousuncertainreflectiveguardedanxiousfearfuloverwhelmedsurprisedfrustratedangrysadhurtdisappointedashamedguiltydisgustedsoftenedrelievedhopefulgratefuljoyfulamused

There are 24 semantic cues. Related emotions can share facial geometry. Everyday warmth and thinking keep the eyes at their normal size.

When to change expression

Set face.update({ expression }) for a text-only response. For spoken segments, subscribe to AvatarSpeech and apply its expression when playback begins.

Example.tsx
import { AvatarSpeech, createFace } from '@allpersonas/core';

export function mountTalkingAssistant(container: HTMLElement) {
  const root = document.createElement('div');
  root.innerHTML = `
    <div data-face style="width:280px;height:340px"></div>
    <button data-speak type="button">Say hello</button>
    <button data-stop type="button">Stop</button>
    <p data-error role="alert"></p>
  `;
  container.append(root);
  const face = createFace(root.querySelector<HTMLElement>('[data-face]')!);
  const speakButton = root.querySelector<HTMLButtonElement>('[data-speak]')!;
  const stopButton = root.querySelector<HTMLButtonElement>('[data-stop]')!;
  const error = root.querySelector<HTMLElement>('[data-error]')!;
  const speech = new AvatarSpeech();
  let disposed = false;
  const unsubscribe = speech.subscribe(() => {
    const state = speech.getSnapshot();
    face.update({
      expression: state.expression,
      speaking: state.speaking,
      boundary: speech.boundary,
    });
    error.textContent = state.error ?? '';
  });

  const sayHello = () => {
    void speech.speak([
      { expression: 'joyful', text: 'It is good to meet you.' },
      { expression: 'curious', text: 'What is on your mind?' },
    ]).catch((cause: unknown) => {
      if (!disposed) error.textContent = cause instanceof Error
        ? cause.message : 'Speech could not start.';
    });
  };
  speakButton.addEventListener('click', sayHello);
  stopButton.addEventListener('click', speech.stop);

  return () => {
    disposed = true;
    speakButton.removeEventListener('click', sayHello);
    stopButton.removeEventListener('click', speech.stop);
    unsubscribe();
    speech.stop();
    face.destroy();
    root.remove();
  };
}

Expressions hold between responses. They are independent of idle blinks and mouth motion. Clear thinking when the response is ready so the selected expression becomes visible.