Overview
Tavus CVI delivers AI-powered video conversations directly in your application. You can integrate it using:
Method | Best For | Complexity | Customization |
---|
iframe | Static websites, quick demos | Low | Low |
Vanilla JS | Basic dynamic behavior | Low | Medium |
Node.js + Express | Backend apps, dynamic embedding | Medium | High |
Daily SDK | Full UI control, advanced features | High | Very High |
Implementation Steps
This is the simplest approach requiring no coding. It leverages Tavus’s prebuilt interface with limited customization options.
- Create a conversation using the Tavus API.
- Replace
YOUR_TAVUS_MEETING_URL
below with your actual conversation URL:
<!DOCTYPE html>
<html>
<head><title>Tavus CVI</title></head>
<body>
<iframe
src="YOUR_TAVUS_MEETING_URL"
allow="camera; microphone; fullscreen; display-capture"
style="width: 100%; height: 500px; border: none;">
</iframe>
</body>
</html>
This is the simplest approach requiring no coding. It leverages Tavus’s prebuilt interface with limited customization options.
- Create a conversation using the Tavus API.
- Replace
YOUR_TAVUS_MEETING_URL
below with your actual conversation URL:
<!DOCTYPE html>
<html>
<head><title>Tavus CVI</title></head>
<body>
<iframe
src="YOUR_TAVUS_MEETING_URL"
allow="camera; microphone; fullscreen; display-capture"
style="width: 100%; height: 500px; border: none;">
</iframe>
</body>
</html>
This method provides basic customizations and dynamic room management for apps without framework.
- Add the following script tag to your HTML
<head>
:
<head>
<script src="https://unpkg.com/@daily-co/daily-js"></script>
</head>
- Use the following script, replacing
'YOUR_TAVUS_MEETING_URL'
with your actual conversation URL:
<body>
<div id="video-call-container"></div>
<script>
// Create a Daily iframe with custom settings
const callFrame = window.Daily.createFrame({
iframeStyle: {
width: '100%',
height: '500px',
},
});
// Join the Tavus CVI meeting
callFrame.join({ url: 'YOUR_TAVUS_MEETING_URL' });
// Append the iframe to the container
document.getElementById('video-call-container').appendChild(callFrame.iframe());
</script>
</body>
This method serves dynamic pages that embed Tavus CVI within Daily rooms.
- Install Express:
- Create
server.js
and implement the following Express server:
const express = require('express');
const app = express();
const PORT = 3000;
app.get('/room', (req, res) => {
const meetingUrl = req.query.url || 'YOUR_TAVUS_MEETING_URL';
res.send(`
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/@daily-co/daily-js"></script>
</head>
<body>
<div id="video-call-container"></div>
<script>
// Create the Daily iframe for the Tavus CVI room
const callFrame = window.Daily.createFrame({
iframeStyle: {
width: '100%',
height: '500px',
},
});
// Join the room
callFrame.join({ url: '${meetingUrl}' });
// Append the iframe to the container
document.getElementById('video-call-container').appendChild(callFrame.iframe());
</script>
</body>
</html>
`);
});
app.listen(PORT, () => console.log(`Server running on http://localhost:${PORT}`));
- Run the server:
- Visit:
http://localhost:3000/room?url=YOUR_TAVUS_MEETING_URL
Notes
- Supports dynamic URLs.
- Can be extended with authentication and other logic using Tavus’s API.
This method offers complete control over the user experience and allows you to build a fully custom interface for Tavus CVI.
- Install SDK:
npm install @daily-co/daily-js
- Use the following script to join the Tavus CVI meeting:
import React, { useEffect, useRef, useState } from 'react';
import DailyIframe from '@daily-co/daily-js';
const getOrCreateCallObject = () => {
// Use a property on window to store the singleton
if (!window._dailyCallObject) {
window._dailyCallObject = DailyIframe.createCallObject();
}
return window._dailyCallObject;
};
const App = () => {
const callRef = useRef(null);
const [remoteParticipants, setRemoteParticipants] = useState({});
useEffect(() => {
// Only create or get one call object per page
const call = getOrCreateCallObject();
callRef.current = call;
// Join meeting
call.join({ url: "YOUR_TAVUS_MEETING_URL" });
// Handle remote participants
const updateRemoteParticipants = () => {
const participants = call.participants();
const remotes = {};
Object.entries(participants).forEach(([id, p]) => {
if (id !== 'local') remotes[id] = p;
});
setRemoteParticipants(remotes);
};
call.on('participant-joined', updateRemoteParticipants);
call.on('participant-updated', updateRemoteParticipants);
call.on('participant-left', updateRemoteParticipants);
// Cleanup
return () => {
call.leave();
};
}, []);
// Attach remote video and audio tracks
useEffect(() => {
Object.entries(remoteParticipants).forEach(([id, p]) => {
// Video
const videoEl = document.getElementById(`remote-video-${id}`);
if (videoEl && p.tracks.video && p.tracks.video.state === 'playable' && p.tracks.video.persistentTrack
) {
videoEl.srcObject = new MediaStream([p.tracks.video.persistentTrack]);
}
// Audio
const audioEl = document.getElementById(`remote-audio-${id}`);
if (
audioEl && p.tracks.audio && p.tracks.audio.state === 'playable' && p.tracks.audio.persistentTrack
) {
audioEl.srcObject = new MediaStream([p.tracks.audio.persistentTrack]);
}
});
}, [remoteParticipants]);
// Custom UI
return (
<div className="min-h-screen bg-gray-900 text-white flex flex-col">
<header className="bg-gray-800 p-4 flex justify-between items-center">
<span className="font-semibold">Meeting Room (daily-js custom UI)</span>
</header>
<main className="flex-1 p-4 grid grid-cols-2 md:grid-cols-4 gap-2">
{Object.entries(remoteParticipants).map(([id, p]) => (
<div
key={id}
className="relative bg-gray-800 rounded-lg overflow-hidden aspect-video w-48"
>
<video
id={`remote-video-${id}`}
autoPlay
playsInline
className="w-1/3 h-1/3 object-contain mx-auto"
/>
<audio id={`remote-audio-${id}`} autoPlay playsInline />
<div className="absolute bottom-2 left-2 bg-black bg-opacity-50 px-2 py-1 rounded text-sm">
{p.user_name || id.slice(-4)}
</div>
</div>
))}
</main>
</div>
);
};
export default App;
- Customize the conversation UI in the script above (Optional). See the Daily JS SDK for details.
FAQs