Embedding Streaming Digital Humans (alpha version)
The documentation listed here is subject to change.
🧠 How to Embed and Control the Unith Widget
This guide explains how to embed the Unith Digital Human (Stream) widget into any website or web app — and how to send messages to it programmatically from external scripts.
You can embed the UNITH stream url as an iframe. This documentation is to give users more control.
1️⃣ Basic Embed Setup
The Unith widget is distributed as a Custom Web Component (<unith-widget>) that you can use in any HTML, React, or Next.js project.
Example (Plain HTML)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Unith Widget Example</title>
<script src="https://embedded-stream.unith.ai/index.js" defer></script>
</head>
<body>
<div style="width: 400px; height: 500px; border-radius: 16px; overflow: hidden;">
<unith-widget
head_id="Public_head-ID"
org_id="YourPublicOrg-ID"
api_key="YOUR_API_KEY_HERE"
username="USER_SPEAKING_TO_DH">
</unith-widget>
</div>
</body>
</html>2️⃣ Styling the Widget
The <unith-widget> element uses a Shadow DOM, so normal CSS cannot reach inside.
You can style around it using a wrapper container.
.unith-container {
width: 400px;
height: 500px;
border-radius: 16px;
overflow: hidden;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
background: #fff;
}💡 If you’re using the floating “chat bubble” version, use:
<unith-widget
....
variant="widget"
placement="bottom-right">
</unith-widget>3️⃣ Sending External Messages to the Chat
Sometimes you may want to programmatically send a message into the chat (e.g. when a user performs an action elsewhere in your app).
The Unith widget doesn’t expose a public JS API yet, but since it uses an open Shadow DOM, you can access its input field and simulate a send.
Full Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Unith Chat Message Test</title>
<script src="https://embedded-stream.unith.ai/index.js" defer></script>
<style>
.unith-container { width: 420px; height: 520px; border-radius: 16px; overflow: hidden; box-shadow: 0 8px 30px rgba(0,0,0,0.12); background: #fff; }
.controls { margin-top: 1rem; }
button { padding: 0.6rem 1rem; border: none; border-radius: 8px; background: #4a90e2; color: white; cursor: pointer; }
</style>
</head>
<body>
<h2>Send Message to Unith Widget</h2>
<div class="unith-container">
<unith-widget
id="unith"
head_id="name-17493"
org_id="orgname-id"
api_key="YOUR_API_KEY_HERE">
</unith-widget>
</div>
<div class="controls">
<button id="send">Send "Hello Samantha"</button>
</div>
<script>
// Utility: wait until input exists
const waitForInput = async (widget, retries = 30) => {
for (let i = 0; i < retries; i++) {
if (widget.shadowRoot) {
const input = widget.shadowRoot.querySelector('input[type="text"]');
if (input) return input;
}
await new Promise(r => setTimeout(r, 300));
}
throw new Error("Chat input not found");
};
async function sendMessageToWidget(text) {
const widget = document.getElementById("unith");
const input = await waitForInput(widget);
input.focus();
input.value = text;
// Trigger the input event so the component updates its state
input.dispatchEvent(new InputEvent("input", {
bubbles: true, cancelable: true, composed: true,
data: text, inputType: "insertText"
}));
// Simulate Enter keypress
["keydown", "keypress", "keyup"].forEach(type =>
input.dispatchEvent(new KeyboardEvent(type, {
key: "Enter", code: "Enter", keyCode: 13, which: 13,
bubbles: true, cancelable: true, composed: true
}))
);
// Try clicking send button if it exists
const btn = widget.shadowRoot.querySelector("button");
if (btn) btn.click();
}
document.getElementById("send").addEventListener("click", () => {
sendMessageToWidget("Hello Samantha");
});
</script>
</body>
</html>✅ How it works
- Waits for the widget to load.
- Accesses its Shadow DOM input.
- Sets the message text.
- Dispatches an
input+Entersequence to mimic user typing. - Falls back to clicking a visible send button if found.
4️⃣ Two-Way Event Communication (Optional)
You can also listen for events or messages coming from the widget:
const widget = document.getElementById('unith');
widget.addEventListener('message', (e) => {
console.log('Widget event:', e.detail);
});
// Example of sending a custom event to the widget
widget.dispatchEvent(new CustomEvent('externalCommand', {
detail: { command: 'sayHello', payload: { text: 'Hi there!' } }
}));Or if the widget runs inside an iframe internally, you can communicate via window.postMessage:
window.addEventListener('message', (event) => {
console.log('Received postMessage from Unith:', event.data);
});5️⃣ Troubleshooting
| Issue | Likely Cause | Fix |
|---|---|---|
| Input not found | Widget not fully loaded yet | Wait longer before accessing shadowRoot |
| Text appears but doesn’t send | Framework ignores synthetic key events | Use InputEvent + click the send button |
| No access to shadowRoot | Widget uses mode: "closed" | Use official Unith API when available |
| Style changes don’t apply | CSS isolation | Wrap widget in a container and style that |
6️⃣ Recommended Best Practice
For production use:
- Wrap all widget control logic in a ready or loaded event listener.
- Keep all direct DOM manipulation in a utility like
sendMessageToWidget(text). - Avoid tight loops; give the widget a small delay to initialize.
- Prefer official APIs once released.
Example Structure Summary
<script src="https://embedded-stream.unith.ai/index.js" defer></script>
<div class="unith-container">
<unith-widget head_id="..." org_id="..." api_key="..."></unith-widget>
</div>
<script>
// your custom integration logic here
</script>