The useOdyssey hook provides a convenient way to manage the Odyssey client lifecycle and state in React applications.
Usage
import { useOdyssey } from '@odysseyml/odyssey/react';
function useOdyssey(options: UseOdysseyOptions): OdysseyClient
Parameters
| Parameter | Type | Required | Description |
|---|
options.apiKey | string | Yes | Your Odyssey API key |
options.handlers | UseOdysseyHandlers | No | Event handlers |
Return Value
The hook returns an OdysseyClient object with the following properties and methods:
Methods
| Method | Type | Description | | |
|---|
connect | () => Promise<MediaStream> | Connect to a session (returns MediaStream when connected, throws on failure) | | |
disconnect | () => void | Disconnect from current session | | |
startStream | (options?: StartStreamOptions) => Promise<string> | Start interactive stream. Supports broadcast: true to enable shared playback | | |
interact | (options: InteractOptions) => Promise<string> | Send interaction prompt | | |
endStream | () => Promise<void> | End current stream | | |
attachToVideo | `(el: HTMLVideoElement | null) => HTMLVideoElement | null` | Attach stream to video element |
State
| Property | Type | Description |
|---|
status | ConnectionStatus | Current connection status |
error | string null | Current error message |
isConnected | boolean | Whether connected and ready |
mediaStream | MediaStream| null | Video/audio stream |
sessionId | string null | Current session ID |
broadcastInfo | BroadcastInfo null | Broadcast info for spectators (if broadcast enabled), or null when not broadcasting |
Basic Example
import { useOdyssey } from '@odysseyml/odyssey/react';
import { useEffect, useRef } from 'react';
function VideoPlayer() {
const videoRef = useRef<HTMLVideoElement>(null);
const odyssey = useOdyssey({
apiKey: 'ody_your_api_key_here',
handlers: {
onConnected: (mediaStream) => {
if (videoRef.current) {
videoRef.current.srcObject = mediaStream;
}
},
onError: (error, fatal) => {
console.error('Error:', error.message, 'Fatal:', fatal);
},
onStreamStarted: (id) => console.log('Started:', id),
onInteractAcknowledged: (prompt) => console.log('Ack:', prompt),
},
});
useEffect(() => {
odyssey.connect()
.then(stream => console.log('Connected with stream:', stream.id))
.catch(err => console.error('Connection failed:', err.message));
return () => odyssey.disconnect();
}, []);
return (
<div>
<video ref={videoRef} autoPlay playsInline muted />
<p>Status: {odyssey.status}</p>
{odyssey.error && <p>Error: {odyssey.error}</p>}
<button onClick={() => odyssey.startStream({ prompt: 'A cat' })}>
Start
</button>
<button onClick={() => odyssey.interact({ prompt: 'Pet the cat' })}>
Interact
</button>
</div>
);
}
Complete Example
import { useEffect, useRef, useState } from 'react';
import { useOdyssey } from '@odysseyml/odyssey/react';
function App() {
const videoRef = useRef<HTMLVideoElement>(null);
const [prompt, setPrompt] = useState('');
const odyssey = useOdyssey({
apiKey: 'ody_your_api_key_here',
handlers: {
onConnected: (mediaStream) => {
if (videoRef.current) {
videoRef.current.srcObject = mediaStream;
videoRef.current.play();
}
},
onDisconnected: () => {
if (videoRef.current) {
videoRef.current.srcObject = null;
}
},
onError: (error, fatal) => {
console.error('Error:', error.message, 'Fatal:', fatal);
},
onStreamStarted: (streamId) => {
console.log('Stream started:', streamId);
},
onInteractAcknowledged: (ackPrompt) => {
console.log('Interaction acknowledged:', ackPrompt);
},
onStreamError: (reason, message) => {
console.error('Stream error:', reason, message);
},
},
});
useEffect(() => {
odyssey.connect()
.then(stream => console.log('Connected with stream:', stream.id))
.catch(err => console.error('Connection failed:', err.message));
return () => odyssey.disconnect();
}, []);
const handleStart = async () => {
await odyssey.startStream({ prompt: 'A cat', portrait: true });
};
const handleInteract = async () => {
if (prompt.trim()) {
await odyssey.interact({ prompt });
setPrompt('');
}
};
const handleEnd = async () => {
await odyssey.endStream();
};
return (
<div>
<video ref={videoRef} autoPlay playsInline muted />
<div>
<p>Status: {odyssey.status}</p>
{odyssey.error && <p style={{ color: 'red' }}>{odyssey.error}</p>}
</div>
<div>
<button onClick={handleStart} disabled={!odyssey.isConnected}>
Start Stream
</button>
<input
value={prompt}
onChange={(e) => setPrompt(e.target.value)}
placeholder="Enter interaction prompt..."
/>
<button onClick={handleInteract} disabled={!odyssey.isConnected}>
Send
</button>
<button onClick={handleEnd} disabled={!odyssey.isConnected}>
End Stream
</button>
</div>
</div>
);
}
Tips
The hook automatically manages cleanup when the component unmounts, but it’s good practice to explicitly call disconnect() in your cleanup function.
- Always call
connect() in a useEffect hook
- Use the returned
status and error properties to show connection state to users
- The
isConnected property is useful for disabling buttons until the connection is ready
Broadcast
The React hook supports Broadcast mode, allowing a single running stream to be shared with multiple connected clients.
To enable Broadcast, pass broadcast: true when starting a stream. Playback details will be delivered through the onBroadcastReady handler.
Streamer Example
import { useOdyssey } from "@odysseyml/odyssey/react";
export function BroadcastExample() {
const odyssey = useOdyssey({
apiKey: process.env.NEXT_PUBLIC_ODYSSEY_API_KEY!,
handlers: {
onConnected: () => {
console.log("Connected");
},
onBroadcastReady: ({ webrtcUrl, spectatorToken, hlsUrl }) => {
console.log("Broadcast ready");
console.log("WebRTC URL:", webrtcUrl);
console.log("Spectator Token:", spectatorToken);
console.log("HLS URL:", hlsUrl);
},
onError: (error, fatal) => {
console.error("Error:", error, "Fatal:", fatal);
},
},
});
const startBroadcast = async () => {
await odyssey.connect();
await odyssey.startStream({
prompt: "A glowing alien ruin at dusk",
broadcast: true,
});
};
return (
<button onClick={startBroadcast}>
Start Broadcast
</button>
);
}
Spectator Example
To connect as a spectator, use the WebRTC URL and spectator token provided by onBroadcastReady.
import { Odyssey } from "@odysseyml/odyssey";
const spectator = await Odyssey.connectToStream(
webrtcUrl,
spectatorToken
);
videoElement.srcObject = spectator.stream;
spectator.onDisconnect(() => {
console.log("Broadcast ended");
});
Broadcast provides shared playback of a single running stream.If your application allows multiple users to send prompts to the same stream, that behavior is implemented through a custom orchestration layer in your application. Broadcast itself does not coordinate or arbitrate user input.