Header Logo

Webflow + WebSocket + TTS: Building a Realtime Product Recommendation Avatar

Webflow + WebSocket + TTS: Building a Realtime Product Recommendation Avatar

Webflow realtime product avatar architecture: WebSocket events, streamed TTS, lip sync, session state, and cancellation handling

Introduction


If you want to put a realtime “recommendation avatar” in a product flow, the hard part is not rendering a face. It’s keeping three independent streams aligned:


  • the user’s text or voice input,

  • the recommendation engine’s response, and

  • the avatar’s audio/video output with low enough latency that it feels synchronous.


This post walks through a practical architecture for building that kind of experience in Webflow using WebSocket-driven updates and TTS. By the end, you should have a clear mental model for how to structure the frontend, where realtime state lives, how to stream assistant text into speech, and how to avoid the usual latency and synchronization traps.


Architecture: what actually needs to be realtime


For a product recommendation avatar, there are usually three separate concerns:


  1. Conversation state — what the user has asked, what products have been considered, what constraints are active.

  2. Response generation — a ranking or LLM layer that produces one or more candidate recommendations.

  3. Presentation — a face that speaks the response, with lip sync and animation that match the audio.


Webflow is just the presentation shell in this setup. You can embed custom JS, but you generally do not want Webflow itself to own your realtime logic. The browser should receive incremental state from your backend over a WebSocket, then render updates and forward text to a speech layer. That keeps your frontend stateless enough to reload cleanly and makes it easier to swap recommendation logic later.


A useful mental model is:


  • WebSocket for server-to-browser events: “recommendation is ready,” “next sentence generated,” “fallback to another product,” “session ended.”

  • TTS for converting the final or partial assistant text into audio.

  • Avatar session for turning that audio into a synchronized talking face.


WebSocket is not there to carry media. It is there to carry control and content events. The media itself should be handled by a purpose-built realtime avatar/voice stack.


Frontend shape in Webflow: keep the browser dumb, not dead


In Webflow, you typically add a small custom embed or page script that does four things:


  1. opens a WebSocket connection to your app backend,

  2. subscribes to session-specific events,

  3. updates the UI when recommendation state changes, and

  4. hands assistant text to the speech/avatar layer.


The important bit is to keep the session identifier stable across reconnections. If the user refreshes the page, you want the frontend to be able to reattach to the same conversational state without replaying the whole flow from scratch.


const ws = new WebSocket(`wss://api.example.com/reco/${sessionId}`);
const ws = new WebSocket(`wss://api.example.com/reco/${sessionId}`);
const ws = new WebSocket(`wss://api.example.com/reco/${sessionId}`);


Two practical points:


  • Use incremental events if your recommendation pipeline supports it. Sending a full blob only after everything is done makes the experience feel slower than it needs to be.

  • Debounce visual changes when upstream systems emit frequent updates. You do not want the UI to flicker because the ranking model is refining itself every 200 ms.


TTS and lip sync: don’t let text and audio diverge


The common mistake in “talking avatar” builds is to treat TTS as a separate side effect after the response is already finalized. That works for a static demo, but it tends to create awkward pauses in production. The better approach is to stream text in chunks and decide explicitly when each chunk becomes speakable.


For example, if your recommendation agent says:


“I’d start with the mid-tier option because it hits your budget, and if you care more about battery life, the premium model is the better fit.”


you do not need to wait for the entire sentence before starting audio. Once the first clause is stable enough, you can send it to TTS, start playback, and continue buffering the next clause.


That said, aggressive streaming has trade-offs:


  • Pros: lower perceived latency, faster turn-taking, better conversational feel.

  • Cons: more complexity around text revisions, sentence boundary detection, and cancellation.


In practice, I recommend one of two strategies:


  1. Sentence-level streaming — wait for punctuation or a stable token window, then synthesize discrete chunks.

  2. Turn-level streaming — use partial text for visual feedback, but only synthesize audio when the response is complete.


Sentence-level streaming is usually the sweet spot for recommendation avatars because it keeps the system responsive without making the speech layer fragile.


Backend orchestration: one session, one source of truth


On the backend, the recommendation service should own session state and emit events to the browser. The browser should not derive state by inference from the UI. If the recommendation engine changes its mind, the backend should publish a correction event rather than expecting the frontend to guess what happened.


A minimal flow looks like this:


  1. User asks for a recommendation.

  2. Backend creates or resumes a session.

  3. Ranking model or LLM produces a recommendation and explanation.

  4. Backend emits events over WebSocket as the response becomes available.

  5. Frontend updates cards, text, and speech playback.


For reliability, make your backend idempotent around session creation and event emission. If the same input arrives twice because the browser retried, you do not want two avatars speaking at once or two recommendation branches competing for the same UI.


Also, think carefully about cancellation. If a user changes their mind while the avatar is mid-explanation, the new turn should preempt the old one. That means you need a way to stop current audio playback and invalidate any pending TTS chunks before the next response starts.


import asyncio<p></p>
import asyncio<p></p>
import asyncio<p></p>


The code above is intentionally simple. In a real system, you would likely split recommendation generation, TTS synthesis, and playback control into separate workers so that slow speech generation does not block the whole turn.


Where Protoface fits: give the voice agent a synchronized face


This is the point where a dedicated avatar layer pays off. Protoface is useful when you want the recommendation agent to speak with a synchronized video face instead of bolting animation onto your own TTS pipeline. For browser-based experiences, the customer-managed iframe embed is the cleanest fit: you can place an interactive avatar on a page without exposing an API key in the browser, and you still keep control over origin allowlisting and session limits.


If your stack already has a voice agent, the other integration point to know about is the LiveKit Agents plugin, which is documented in the relevant repo and PyPI package. That path is useful when the avatar should follow the agent’s audio in real time rather than sitting beside it as a separate component. See the docs at docs.protoface.com for the exact session and avatar fields, plus the current authentication and embed options.


import requests<p></p>
import requests<p></p>
import requests<p></p>


The exact request shape depends on the current API version, so treat the snippet as illustrative. The important architectural point is that the backend should own creation of the avatar session, and the browser should consume a scoped session representation rather than direct long-lived credentials.


Putting it together in a Webflow page


A pragmatic implementation on a Webflow site is:


  1. Embed a placeholder container for the avatar and recommendation cards.

  2. Load a small client script that opens a WebSocket to your backend.

  3. On user interaction, send the query to your backend, not directly to TTS or the avatar.

  4. Let the backend publish recommendation events and speech text.

  5. Render product cards immediately, and let the avatar speak as soon as the first stable chunk is available.


If you use an iframe-based avatar embed, the integration gets even simpler because the avatar surface can live in its own origin boundary. That reduces frontend coupling and keeps secrets out of Webflow entirely. The trade-off is that you have to think about message passing and session coordination a little more explicitly, but that is usually worth it.


One thing to avoid is binding speech synthesis directly to click handlers in the Webflow page. That makes the UI look local but the system behave globally. If anything fails after the click, you end up with half-updated state and no reliable recovery path. Keep all authoritative turn logic on the backend, and treat the browser as a renderer plus event sink.


Trade-offs and gotchas


A few issues show up repeatedly in realtime avatar builds:


  • Latency stacking: model inference, network round trips, TTS generation, and video sync all add up. Shaving 150 ms off each layer matters more than over-optimizing one layer.

  • Chunk boundaries: poor sentence segmentation makes speech sound robotic or prematurely cut off.

  • Cancellation: users interrupt agents constantly; design for stop/restart from day one.

  • State drift: if the UI, backend, and audio pipeline each maintain their own notion of “current response,” they will eventually disagree.


If you solve those, the rest is mostly integration work.


Conclusion


The cleanest way to build a realtime product recommendation avatar is to separate concerns: use WebSocket events for conversational state, use TTS for speech generation, and keep the avatar/lip-sync layer focused on synchronized presentation. In Webflow, that means a thin client and a backend that owns session state, cancellation, and streaming decisions.


If you want to accelerate the avatar side of this stack, start with the docs and one of the quickstarts in the Protoface ecosystem, then wire it into your own session orchestration. The practical next step is simple: define your event schema first, then choose whether the browser should connect through an iframe embed or through a voice-agent integration.


For implementation details and current API shapes, see docs.protoface.com.


Add a face to your AI.

No credit card needed.

Add a face to your AI.

No credit card needed.

Add a face to your AI.

No credit card needed.