Skip to content

Repository files navigation

Avatar Chat Widget

Embeddable 3D Avatar Chat Widget - Real-time Voice & Text Chat with Gaussian Splatting Avatar Animation.

npm version

Next-Gen Engagement: Replace static chatbots with a living, breathing 3D avatar that talks to your visitors.

Features

  • Hyper-Realistic 3D Avatars: Powered by 3D Gaussian Splatting for cinematic visual fidelity directly in the browser.
  • Precise Lip-Sync: Animation tightly synchronized with audio for a lifelike experience.
  • Natural Voice Interaction: Full duplex voice chat with echo cancellation and noise suppression.
  • Zero-Conflict styles: Fully encapsulated using Shadow DOM – never breaks your site's layout.
  • Smart Loading: Lazy-loaded 3D engine ensures your initial page load remains instant.
  • 100% Customizable: Change suggestions, colors, and behaviors via simple config.

Quick Start

Script Tag (No Build Required)

Add directly to any HTML page or website builder:

<!-- Container for the widget -->
<div id="avatar-chat"></div>

<!-- Load from CDN -->
<script src="https://cdn.jsdelivr.net/npm/@myned-ai/avatar-chat-widget"></script>

<script>
  AvatarChat.init({
    container: '#avatar-chat',
    serverUrl: 'wss://your-backend-server.com/ws',
    position: 'bottom-right'
  });
</script>

NPM Package

npm install @myned-ai/avatar-chat-widget
import { AvatarChat } from '@myned-ai/avatar-chat-widget';
import '@myned-ai/avatar-chat-widget/style.css';

const chat = AvatarChat.init({
  container: '#avatar-chat',
  serverUrl: 'wss://your-backend-server.com/ws',
  onReady: () => console.log('Widget ready!'),
  onMessage: (msg) => console.log('Message:', msg)
});

// Control programmatically
chat.sendMessage('Hello!');
chat.collapse(); // Minimize to bubble
chat.expand();   // Open full widget
chat.destroy();  // Cleanup

Renderer-Only Mount

Need just the 3D avatar, driven by your own pipeline — no chat UI, no voice loop, no WebSocket? mountAvatar returns the avatar controller directly:

import { mountAvatar } from '@myned-ai/avatar-chat-widget';
// or from a script tag: AvatarChat.mountAvatar(...)

const avatar = mountAvatar({ container: '#stage' });

avatar.setChatState('Listening');          // Idle | Listening | Responding
avatar.enableLiveBlendshapes();
avatar.updateBlendshapes({ jawOpen: 0.4 }); // 52 ARKit weights, ~30 FPS
avatar.updateAudioRMS?.(0.12);              // audio-gated gaze (optional)
avatar.setBackgroundColor('#22d3ee');       // 3D scene backdrop, runtime
avatar.dispose();                           // unmount
Option Type Default Description
container string | HTMLElement required CSS selector or DOM element
avatarUrl string bundled Nyx URL to avatar ZIP file
backgroundColor string '#ffffff' Initial 3D scene background (hex)
backgroundImage string undefined Image drawn behind the avatar in the 3D scene
renderSize number 800 Internal render resolution, px — CSS-scaled to fit the container
onReady / onError functions undefined Renderer loaded / failed to load

Loading is lazy (placeholder first, renderer chunk in the background), and multiple mounts are allowed. The returned controller implements IAvatarController (exported, with ChatState).


Configuration

Option Type Default Description
container string | HTMLElement required CSS selector or DOM element
serverUrl string required WebSocket server URL (ws:// or wss://)
position string 'bottom-right' bottom-right, bottom-left, top-right, top-left, inline
primaryColor string '#4B4ACF' Main brand color (hex, rgb, etc)
suggestions string[] ['...'] Array of quick-reply suggestions
startCollapsed boolean true Start minimized as bubble
tooltipText string 'Hi!...' Text shown on bubble hover
width number 380 Widget width (200-2000px)
height number 550 Widget height (300-2000px)
enableVoice boolean true Enable voice chat
enableText boolean true Enable text chat
authEnabled boolean false Enable HMAC authentication
avatarUrl string auto-detected URL to avatar ZIP file
backgroundImage string undefined Image drawn behind the avatar in the 3D stage (URL or path relative to assetsBaseUrl)
assetsBaseUrl string auto-detected Base URL for worklet/assets
customStyles string undefined Custom CSS to inject into Shadow DOM
logLevel string 'error' none, error, warn, info, debug

Callbacks

Callback Type Description
onReady () => void Widget initialized and ready
onConnectionChange (connected: boolean) => void WebSocket connection status changed
onMessage (msg: {role, text}) => void Message received from server
onError (error: Error) => void Error occurred

Customization

Quick Branding

For simple color matching, use the primaryColor option:

AvatarChat.init({
  container: '#avatar-chat',
  serverUrl: 'wss://...',
  primaryColor: '#FF5722', // Match your brand
  secondaryColor: '#37474F'
});

Avatar Stage Background

To show an image behind the avatar instead of the default flat background:

AvatarChat.init({
  container: '#avatar-chat',
  serverUrl: 'wss://...',
  backgroundImage: '/asset/office.png', // absolute URL, or path resolved against assetsBaseUrl
});

Mist Overlay

The avatar-focus view has a soft mist fade at the bottom of the stage. It is currently disabled by default. To re-enable it, uncomment the two display: block rules for .avatar-mist-overlay in src/widget/styles.ts (desktop and mobile) and rebuild.

Advanced Styling

For deeper customization, use the customStyles option to inject CSS directly into the widget's Shadow DOM:

AvatarChat.init({
  container: '#avatar-chat',
  serverUrl: 'wss://your-server.com/ws',
  customStyles: `
    /* Primary brand colors (gradient, buttons, user messages) */
    .chat-bubble,
    .chat-header {
      background: linear-gradient(135deg, #ff6b6b 0%, #ee5a6f 100%) !important;
    }

    .input-button {
      background: #ff6b6b !important;
    }

    .input-button:hover:not(:disabled) {
      background: #ee5a6f !important;
    }

    .message.user .message-bubble {
      background: #ff6b6b !important;
    }

    /* Avatar border */
    .avatar-circle {
      border-color: #ff6b6b !important;
    }

    /* Input focus color */
    #chatInput:focus {
      border-color: #ff6b6b !important;
    }
  `
});

Common color targets:

  • .chat-bubble - Minimized bubble
  • .chat-header - Header gradient
  • .avatar-circle - Avatar border
  • .input-button - Send & mic buttons
  • .message.user .message-bubble - User message bubbles
  • #chatInput:focus - Input field focus state
  • .avatar-subtitles - Subtitle pill in avatar view (background, text color, position)

Features

  • Text & Voice Chat - Real-time messaging with microphone streaming
  • 3D Avatar - Gaussian Splatting rendering with 52 ARKit blendshapes
  • Synchronized Animation - Audio and facial expressions in perfect sync
  • Auto-reconnection - Resilient WebSocket with exponential backoff
  • Shadow DOM - Complete CSS isolation, no style conflicts
  • Framework Agnostic - Works with React, Vue, Angular, or vanilla HTML
  • Accessible - WCAG 2.1 AA compliant with ARIA labels

WebSocket Protocol

Client → Server

{ "type": "text", "data": "Hello", "userId": "user_123", "timestamp": 1234567890 }
{ "type": "audio", "data": "<ArrayBuffer>", "format": "audio/webm" }

Server → Client

{ "type": "audio_start", "sessionId": "abc", "sampleRate": 24000 }
{ "type": "audio_chunk", "data": "<ArrayBuffer>", "timestamp": 1234567890 }
{ "type": "blendshape", "weights": {...}, "timestamp": 1234567890 }
{ "type": "audio_end", "sessionId": "abc" }
{ "type": "text", "data": "Hello", "timestamp": 1234567890 }

Authentication

Disabling Auth (Development)

For local testing without an auth server:

AvatarChat.init({
  container: '#avatar-chat',
  serverUrl: 'ws://localhost:8080/ws',
  authEnabled: false  // Disable authentication
});

Production Setup

The widget uses HMAC-SHA256 token authentication for secure connections:

  1. Widget requests token from POST /api/auth/token
  2. Server validates origin and returns signed token
  3. Widget connects with token: wss://server/ws?token=...
  4. Server verifies signature and expiration

Security features:

  • Origin validation (whitelist domains)
  • Time-limited tokens with auto-refresh
  • Rate limiting per domain/session

Development

Local Setup

# Install dependencies
npm install

# Start dev server with hot reload
npm run dev

# Build for production
npm run build:lib

Testing with Backend

# Clone sample server
git clone https://github.com/myned-ai/avatar-chat-server.git
cd avatar-chat-server

# Start with Docker
docker-compose up

Server runs on ws://localhost:8765 by default.


Troubleshooting

Widget not loading

  • Check browser console for errors
  • Verify serverUrl is correct WebSocket URL (ws:// or wss://)
  • Ensure container element exists before calling init()

Avatar not rendering

  • Check server CORS headers (COEP/COOP)
  • Verify avatar ZIP file is accessible
  • Check logLevel: 'debug' for detailed logs

Voice not working

  • Microphone permission required
  • HTTPS required for production (getUserMedia)
  • Check browser compatibility

Acknowledgements

Inspired by excellent research in the avatar space: OpenLRM, GAGAvatar, GaussianAvatars, VHAP.


License

Code: MIT — see LICENSE.

The bundled demo avatar (public/asset/nyx.zip) is adapted from LAM output (with our own skeleton re-weighting and animation set). LAM's code is Apache-2.0; its sample assets, including the animation data used here, are distributed under MIT via LAM_WebRender. LAM's model weights are CC BY-NC 4.0 but are not part of this package and place no conditions on generated output.

About

Embeddable 3D Avatar Chat Widget

Resources

Stars

13 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages