Spaces:
Paused
Paused
| title: Gemma Avatar | |
| emoji: π£οΈ | |
| colorFrom: indigo | |
| colorTo: purple | |
| sdk: docker | |
| app_port: 7860 | |
| pinned: false | |
| short_description: Talk to Gemma 4 face to face, with a 3D lip-synced avatar | |
| models: | |
| - google/gemma-4-31B-it | |
| - nvidia/parakeet-tdt-1.1b | |
| - Qwen/Qwen3-TTS-12Hz-1.7B-CustomVoice | |
| # Gemma Avatar | |
| Realtime voice chat with a 3D talking-head avatar. Same AI stack as the | |
| [smolagents/hf-realtime-voice](https://huggingface.co/spaces/smolagents/hf-realtime-voice) | |
| Space, but the orb visualization is replaced by a [TalkingHead](https://github.com/met4citizen/TalkingHead) | |
| 3D avatar with real-time audio-driven lip-sync. | |
| > **Rebuild note**: this Space was reconstructed after the original repo was | |
| > lost. The frontend (avatar, worklets, WebSocket client, HeadAudio lip-sync) | |
| > is a faithful recovery, vendored from `smolagents/hf-realtime-voice` and | |
| > `met4citizen/HeadAudio`. What's genuinely new: the avatar model itself | |
| > (generated by [MeshForge](https://huggingface.co/spaces/Daankular/meshforge-avatar) | |
| > from a real photo, not the original's Ready Player Me `brunette.glb`), and | |
| > the backend β this rebuild ships **direct-connect only** (no | |
| > load-balancer session proxy), since that needs backend credentials that | |
| > weren't recoverable. Paste a running `ws://.../v1/realtime` endpoint into | |
| > Settings to talk to it. | |
| ## The pipeline | |
| ``` | |
| you speak β silero-VAD β parakeet-tdt-1.1b (STT) β gemma-4-31B-it on Cerebras β Qwen3-TTS β avatar speaks | |
| ``` | |
| Transport is the OpenAI Realtime GA protocol over WebSocket against an s2s | |
| (speech-to-speech) backend: mic PCM16 @ 16 kHz goes up as | |
| `input_audio_buffer.append`, TTS PCM16 @ 16 kHz comes back as | |
| `response.output_audio.delta`, transcripts stream alongside. | |
| ## How the avatar works | |
| - **Rendering / body language** β [TalkingHead](https://github.com/met4citizen/TalkingHead) | |
| (three.js). Blinking, breathing, idle sway, moods, hand gestures, and emoji | |
| expressions are its built-in animation system. | |
| - **Lip-sync** β the backend sends raw PCM only (no word timings, no visemes), | |
| so the mouth is driven from the audio itself with | |
| [HeadAudio](https://github.com/met4citizen/HeadAudio): an AudioWorklet that | |
| classifies MFCC frames into Oculus visemes (~50 ms latency, fully in-browser). | |
| The s2s playback worklet is routed into TalkingHead's audio graph | |
| (`audioAnalyzerNode β audioSpeechGainNode β reverb β speakers`) and HeadAudio | |
| taps the speech gain node. | |
| - **The model plays the avatar** β three function tools are declared to the | |
| backend: `set_mood`, `make_hand_gesture`, `make_facial_expression`. Gemma | |
| calls them mid-conversation (smiles when greeting, shrugs when unsure, | |
| thumbs-up when agreeing). | |
| - **Choreography** β client statuses drive presence: the avatar makes eye | |
| contact when you start talking, gestures with its hands on new utterances, | |
| and barge-in clears the playback buffer so the mouth settles instantly. | |
| ## Run it | |
| ```bash | |
| bun install | |
| bun run dev | |
| ``` | |
| Open http://localhost:7860, tap the settings icon, paste a direct | |
| `ws://.../v1/realtime` endpoint URL, then tap **Start talking**. | |
| `?fakemic=1` starts a session with a silent synthetic mic (useful for testing | |
| the full loop without a microphone β trigger a reply from the console with | |
| `getClient().requestResponse()`). | |
| ## Layout | |
| ``` | |
| index.ts Bun server: HTML import + static assets + /api/config | |
| index.html App shell (avatar hero, caption, subtitles, settings) | |
| src/app.js Session wiring, tool executor, UI state | |
| src/avatar.js AvatarStage: TalkingHead + HeadAudio + choreography | |
| src/s2s/s2s-ws-client.js Realtime WS client (vendored from smolagents/hf-realtime-voice; | |
| orb removed, injectable output node + worklet base URL + shared-ctx close) | |
| src/s2s/codec.js PCM/base64 + transcript helpers (vendored, unchanged) | |
| src/vendor/headaudio.min.mjs HeadAudio node class (vendored, unchanged) | |
| public/worklets/ mic-capture + audio-playback AudioWorklets (vendored, unchanged) | |
| public/vendor/ HeadAudio worklet processor + viseme model (vendored, unchanged) | |
| public/avatars/meshforge.glb Default avatar (generated from a photo by MeshForge) | |
| ``` | |
| ## Notes | |
| - The avatar GLB must have a Mixamo-compatible rig plus ARKit and Oculus-viseme | |
| blend shapes β see [Daankular/meshforge-avatar](https://huggingface.co/spaces/Daankular/meshforge-avatar) | |
| for how this one was generated. | |
| - TalkingHead owns the AudioContext; the s2s client is handed `head.audioCtx` | |
| and never closes it. | |
| - Everything animation-related runs on requestAnimationFrame β a backgrounded | |
| tab freezes the avatar (audio keeps playing). | |