Simple Client-Side Example
Complete working example using SDK-based control (client-side only).
For Demos and Prototypes
This approach is perfect for demos, prototypes, and learning. For production, see Server Controlled Example.
Complete HTML + JavaScript Example
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Kaltura Avatar - Simple Example</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 800px;
margin: 50px auto;
padding: 20px;
}
#avatar-container {
width: 512px;
height: 512px;
border: 2px solid #333;
border-radius: 12px;
overflow: hidden;
background: #000;
margin: 20px 0;
}
.controls {
display: flex;
gap: 10px;
margin: 20px 0;
}
button {
padding: 12px 24px;
font-size: 16px;
border: none;
border-radius: 6px;
cursor: pointer;
transition: background 0.3s;
}
button:hover:not(:disabled) {
opacity: 0.9;
}
button:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.primary {
background: #007bff;
color: white;
}
.danger {
background: #dc3545;
color: white;
}
.warning {
background: #ffc107;
color: black;
}
#status {
padding: 12px;
border-radius: 6px;
margin: 20px 0;
font-weight: bold;
}
#status.idle {
background: #e9ecef;
}
#status.creating {
background: #fff3cd;
color: #856404;
}
#status.ready {
background: #d4edda;
color: #155724;
}
#status.error {
background: #f8d7da;
color: #721c24;
}
textarea {
width: 100%;
height: 100px;
padding: 10px;
font-size: 14px;
border: 2px solid #ddd;
border-radius: 6px;
resize: vertical;
}
.error-message {
color: #721c24;
background: #f8d7da;
padding: 10px;
border-radius: 6px;
margin: 10px 0;
display: none;
}
</style>
</head>
<body>
<h1>Kaltura Avatar - Simple Example</h1>
<div id="status" class="idle">Not started</div>
<!-- Avatar display -->
<div id="avatar-container"></div>
<!-- Controls -->
<div class="controls">
<button id="start-btn" class="primary">Start Avatar</button>
<button id="end-btn" class="danger" disabled>End Session</button>
</div>
<!-- Text input -->
<div>
<textarea id="text-input" placeholder="Enter text for avatar to speak...">Hello! I am your Kaltura Avatar. How can I help you today?</textarea>
<div class="controls">
<button id="speak-btn" class="primary" disabled>Make Avatar Speak</button>
<button id="interrupt-btn" class="warning" disabled>Interrupt</button>
</div>
</div>
<div id="error-message" class="error-message"></div>
<script type="module" src="main.js"></script>
</body>
</html>
main.js
import { KalturaAvatarSession } from '@unisphere/models-sdk-js';
// Configuration
const KS = 'your-api-key-here'; // TODO: Replace with your Kaltura Session
const BASE_URL = 'https://api.avatar.us.kaltura.ai/v1/avatar-session';
const AVATAR_ID = 'avatar-123';
const VOICE_ID = 'voice-456';
// UI Elements
const statusEl = document.getElementById('status');
const avatarContainer = document.getElementById('avatar-container');
const startBtn = document.getElementById('start-btn');
const endBtn = document.getElementById('end-btn');
const speakBtn = document.getElementById('speak-btn');
const interruptBtn = document.getElementById('interrupt-btn');
const textInput = document.getElementById('text-input');
const errorMessageEl = document.getElementById('error-message');
// Avatar session
let session = null;
// Update status
function setStatus(status, message) {
statusEl.textContent = message;
statusEl.className = status;
}
// Show error
function showError(message) {
errorMessageEl.textContent = message;
errorMessageEl.style.display = 'block';
setTimeout(() => {
errorMessageEl.style.display = 'none';
}, 5000);
}
// Enable/disable controls
function updateControls(state) {
startBtn.disabled = state !== 'idle';
endBtn.disabled = state !== 'ready';
speakBtn.disabled = state !== 'ready';
interruptBtn.disabled = state !== 'ready';
}
// Start avatar
startBtn.addEventListener('click', async () => {
try {
setStatus('creating', 'Creating avatar session...');
updateControls('creating');
// Create SDK instance
session = new KalturaAvatarSession(API_KEY, {
baseUrl: BASE_URL,
logLevel: 'info',
});
// Setup event listeners
session.on('stateChange', (state) => {
console.log('Session state:', state);
switch (state) {
case 'READY':
setStatus('ready', `Avatar ready! Session: ${session.getSessionId()}`);
updateControls('ready');
break;
case 'ENDED':
setStatus('idle', 'Session ended');
updateControls('idle');
session = null;
break;
case 'ERROR':
setStatus('error', 'Session error occurred');
updateControls('idle');
break;
}
});
session.on('connectionChange', (state) => {
console.log('Connection state:', state);
});
session.on('error', (error) => {
console.error('Avatar error:', error.code, error.message);
showError(`Error: ${error.message}`);
setStatus('error', 'Error occurred');
updateControls('idle');
});
// Create session with auto-attach
await session.createSession({
avatarId: AVATAR_ID,
voiceId: VOICE_ID,
videoContainerId: 'avatar-container',
});
console.log('✅ Avatar started!');
// Welcome message
await session.sayText('Hello! I am ready to assist you.');
} catch (error) {
console.error('Failed to start avatar:', error);
showError(`Failed to start avatar: ${error.message}`);
setStatus('error', 'Failed to start avatar');
updateControls('idle');
}
});
// Make avatar speak
speakBtn.addEventListener('click', async () => {
if (!session) {
showError('No active session');
return;
}
const text = textInput.value.trim();
if (!text) {
showError('Please enter text');
return;
}
try {
await session.sayText(text);
console.log('Avatar speaking:', text);
} catch (error) {
console.error('Failed to make avatar speak:', error);
showError(`Failed: ${error.message}`);
}
});
// Interrupt avatar
interruptBtn.addEventListener('click', async () => {
if (!session) {
showError('No active session');
return;
}
try {
await session.interrupt();
console.log('Avatar interrupted');
} catch (error) {
console.error('Failed to interrupt:', error);
showError(`Failed to interrupt: ${error.message}`);
}
});
// End session
endBtn.addEventListener('click', async () => {
if (!session) {
showError('No active session');
return;
}
try {
await session.endSession();
console.log('Session ended');
setStatus('idle', 'Session ended');
updateControls('idle');
session = null;
} catch (error) {
console.error('Failed to end session:', error);
showError(`Failed to end session: ${error.message}`);
}
});
// Cleanup on page unload
window.addEventListener('beforeunload', () => {
if (session) {
session.endSession();
}
});
// Initial state
updateControls('idle');
Running the Example
1. Install Dependencies
npm install @unisphere/models-sdk-js
2. Setup Build Tool (Vite)
npm install -D vite
package.json
{
"name": "avatar-demo",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build"
},
"dependencies": {
"@unisphere/models-sdk-js": "latest"
},
"devDependencies": {
"vite": "latest"
}
}
3. Update Configuration
In main.js, replace:
API_KEY- Your Kaltura Kaltura SessionBASE_URL- Your API endpointAVATAR_ID- Your avatar IDVOICE_ID- Your voice ID
4. Run
npm run dev
Open http://localhost:5173 in your browser.
Features Demonstrated
✅ Session creation with auto-attach ✅ Event handling (state, connection, error) ✅ Making avatar speak ✅ Interrupting speech ✅ Ending session ✅ Error handling ✅ UI state management
Next Steps
- Server Controlled Example - Production-ready approach
- React Integration - Use with React
- Error Recovery - Robust error handling