My voice agent sounded great and looked like nothing. It was a pulsing circle on a screen, which is fine for a prototype and a bit sad when you're showing it to someone.
I wanted a face. Today I tried OpenSpline, a tool that puts a live avatar on top of a voice agent. It has a hosted Cloud option, but I have a decent NVIDIA card sitting under my desk, so I went the self-host route. I had a talking avatar in about 15 minutes.
What I started with
- A Windows machine running WSL2
- An NVIDIA GPU
- A Python voice agent that already worked
- One portrait photo
The GPU is the one thing you can't skip. OpenSpline's self-hosted service needs an NVIDIA card on Linux or WSL2. AMD and Apple GPUs aren't supported, so check that before you start.
Step 1: Install and start the service
This was two commands:
curl -fsSL https://raw.githubusercontent.com/openspline/openspline/main/install.sh | sh sh ~/.local/share/openspline/run.sh The first sets everything up and the second starts the service. The installer picks the right PyTorch build for your card, with CUDA 12.8 for Blackwell and 12.6 for older NVIDIA GPUs, so I didn't have to think about drivers and wheels.
As with anything you pipe into sh, I skimmed the script before running it, and you should too.
Then I opened http://localhost:7860, and a working demo was already there. This was the moment I knew it was worth continuing. I hadn't written a line of code, and I was already looking at a face that talked back.
Step 2: Plug it into my own agent
The demo is nice, but I wanted my agent to use it. The Python SDK was a single install:
pip install openspline Then this is the whole integration:
from openspline import Openspline async with Openspline().avatar("portrait.jpg", quality="low") as avatar: print(avatar.viewer_url) await avatar.stream(agent.audio_stream(), sample_rate=24000) Here's what that does:
- It takes a still photo as the avatar's face.
- It lets you pick a quality level. I started on
lowto get the pipeline working. - It takes whatever audio stream your agent produces.
- It prints a viewer URL. I opened it and watched my agent speak through the portrait.
There was nothing to configure, because the SDK defaults to the local service at http://localhost:7860. The only thing I checked was the sample rate. I matched it to my agent's audio output (24 kHz), and everything lined up.
What I liked
It's genuinely local. My audio and my portrait never leave my machine. There's no API key to manage and no usage meter running while I experiment. For a side project, that makes a difference.
The SDK is tiny. The core of it is one context manager and one stream() call. I didn't need a new architecture, just a few lines around the audio stream I already had.
It starts from a single photo. No 3D modeling, rigging or recording sessions. I gave it a portrait and got a speaking avatar.
There's room to grow. The docs cover things I haven't touched yet: multiple GPUs, a High quality mode, photo and video backgrounds, React and browser embeds, and connectors for OpenAI Realtime, LiveKit, Gemini Live and ElevenLabs.
Things to know before you try it
- NVIDIA only. If you're on AMD or a Mac, the Cloud option is probably your route instead.
- Begin with
quality="low". Prove the pipeline end to end first, then turn quality up once you know it works. - Check your audio format. Mismatched sample rates are the most common way to make a voice pipeline sound wrong, so confirm yours before debugging anything else.
Where I'm taking it next
Now that the basics work, I want to try a custom background behind the portrait, embed the avatar in a web page, and see how it does with a longer, more natural conversation. A face changes how a voice agent feels. It stops being a tool you talk at and starts feeling like someone you talk to.
If you have an NVIDIA GPU and an agent that already talks, this is a good afternoon project, and you may finish it before the afternoon is over.


