The Realtime API enables you to transform live video streams with minimal latency using WebRTC. Perfect for building camera effects, video conferencing filters, VR/AR applications, and interactive live streaming.
Quick Start
import { createDecartClient , models } from "@decartai/sdk" ;
const model = models . realtime ( "mirage" );
// Get user's camera stream
const stream = await navigator . mediaDevices . getUserMedia ({
audio: true ,
video: {
frameRate: model . fps ,
width: model . width ,
height: model . height ,
},
});
// Create client and connect
const client = createDecartClient ({
apiKey: "your-api-key-here" ,
});
const realtimeClient = await client . realtime . connect ( stream , {
model ,
onRemoteStream : ( transformedStream ) => {
videoElement . srcObject = transformedStream ;
},
initialState: {
prompt: {
text: "Anime" ,
enrich: true ,
},
},
});
// Change style on the fly
realtimeClient . setPrompt ( "Cyberpunk city" );
// Disconnect when done
realtimeClient . disconnect ();
Connecting
Getting Camera Access
Request access to the user’s camera using the WebRTC getUserMedia API:
const stream = await navigator . mediaDevices . getUserMedia ({
audio: true ,
video: {
frameRate: 25 ,
width: 1280 ,
height: 704 ,
},
});
Use the model’s fps
, width
, and height
properties to ensure optimal performance.
Establishing Connection
Connect to the Realtime API with your media stream:
const realtimeClient = await client . realtime . connect ( stream , {
model: models . realtime ( "mirage" ),
onRemoteStream : ( transformedStream : MediaStream ) => {
// Display the transformed video
const videoElement = document . getElementById ( "output-video" );
videoElement . srcObject = transformedStream ;
},
initialState: {
prompt: {
text: "Lego World" ,
enrich: true , // Let Decart enhance the prompt (recommended)
},
mirror: false , // Set to true for front-facing cameras
},
});
Parameters:
stream
(required) - MediaStream from getUserMedia
model
(required) - Realtime model from models.realtime()
onRemoteStream
(required) - Callback that receives the transformed video stream
initialState.prompt
(required) - Initial style prompt
text
- Style description
enrich
- Whether to auto-enhance the prompt (default: true)
initialState.mirror
(optional) - Enable mirror mode for front-facing cameras
Managing Prompts
Change the transformation style dynamically without reconnecting:
// Simple prompt with automatic enhancement
realtimeClient . setPrompt ( "Anime style" );
// Custom detailed prompt without enhancement
realtimeClient . setPrompt (
"A detailed artistic style with vibrant colors and dramatic lighting" ,
{ enrich: false }
);
Parameters:
prompt
(required) - Text description of desired style
options.enrich
(optional) - Whether to enhance the prompt (default: true)
Prompt enhancement uses Decart’s AI to expand simple prompts for better results. Disable it if you want full control over the exact prompt.
Camera Mirroring
Toggle horizontal mirroring, useful for front-facing cameras:
// Enable mirror mode
realtimeClient . setMirror ( true );
// Disable mirror mode
realtimeClient . setMirror ( false );
This flips the video horizontally, making front-facing camera views feel more natural to users.
Connection State
Monitor and react to connection state changes:
// Check state synchronously
const isConnected = realtimeClient . isConnected (); // boolean
const state = realtimeClient . getConnectionState (); // "connected" | "connecting" | "disconnected"
// Listen to state changes
realtimeClient . on ( "connectionChange" , ( state ) => {
console . log ( `Connection state: ${ state } ` );
if ( state === "disconnected" ) {
// Handle disconnection
showReconnectButton ();
} else if ( state === "connected" ) {
// Handle successful connection
hideReconnectButton ();
}
});
Use this to update your UI and handle reconnection logic.
Error Handling
Handle errors with the error event:
import type { DecartSDKError } from "@decartai/sdk" ;
realtimeClient . on ( "error" , ( error : DecartSDKError ) => {
console . error ( "SDK error:" , error . code , error . message );
switch ( error . code ) {
case "INVALID_API_KEY" :
showError ( "Invalid API key. Please check your credentials." );
break ;
case "WEB_RTC_ERROR" :
showError ( "Connection error. Please check your network." );
break ;
case "MODEL_NOT_FOUND" :
showError ( "Model not found. Please check the model name." );
break ;
default :
showError ( `Error: ${ error . message } ` );
}
});
Error Codes:
INVALID_API_KEY
- API key is invalid or missing
WEB_RTC_ERROR
- WebRTC connection failed
MODEL_NOT_FOUND
- Specified model doesn’t exist
INVALID_INPUT
- Invalid input parameters
Session Management
Access the current session ID:
const sessionId = realtimeClient . sessionId ;
console . log ( `Current session: ${ sessionId } ` );
This can be useful for logging, analytics, or debugging.
Cleanup
Always disconnect when done to free up resources:
// Disconnect from the service
realtimeClient . disconnect ();
// Remove event listeners
realtimeClient . off ( "connectionChange" , onConnectionChange );
realtimeClient . off ( "error" , onError );
// Stop the local media stream
stream . getTracks (). forEach ( track => track . stop ());
Failing to disconnect can leave WebRTC connections open and waste resources.
Complete Example
Here’s a full application with all features:
import { createDecartClient , models , type DecartSDKError } from "@decartai/sdk" ;
async function setupRealtimeVideo () {
try {
// Get camera stream with optimal settings
const model = models . realtime ( "mirage" );
const stream = await navigator . mediaDevices . getUserMedia ({
audio: true ,
video: {
frameRate: model . fps ,
width: model . width ,
height: model . height ,
},
});
// Display input video
const inputVideo = document . getElementById ( "input-video" ) as HTMLVideoElement ;
inputVideo . srcObject = stream ;
// Create client
const client = createDecartClient ({
apiKey: process . env . DECART_API_KEY ,
});
// Connect to Realtime API
const realtimeClient = await client . realtime . connect ( stream , {
model ,
onRemoteStream : ( transformedStream ) => {
const outputVideo = document . getElementById ( "output-video" ) as HTMLVideoElement ;
outputVideo . srcObject = transformedStream ;
},
initialState: {
prompt: {
text: "Studio Ghibli animation style" ,
enrich: true ,
},
mirror: true , // Using front camera
},
});
// Handle connection state changes
realtimeClient . on ( "connectionChange" , ( state ) => {
const statusElement = document . getElementById ( "status" );
statusElement . textContent = `Status: ${ state } ` ;
statusElement . className = `status- ${ state } ` ;
});
// Handle errors
realtimeClient . on ( "error" , ( error : DecartSDKError ) => {
console . error ( "Realtime error:" , error );
const errorElement = document . getElementById ( "error" );
errorElement . textContent = error . message ;
errorElement . style . display = "block" ;
});
// Allow user to change styles
const styleInput = document . getElementById ( "style-input" ) as HTMLInputElement ;
styleInput . addEventListener ( "change" , ( e ) => {
const prompt = ( e . target as HTMLInputElement ). value ;
realtimeClient . setPrompt ( prompt , { enrich: true });
});
// Toggle mirror mode
const mirrorToggle = document . getElementById ( "mirror-toggle" ) as HTMLInputElement ;
mirrorToggle . addEventListener ( "change" , ( e ) => {
const enabled = ( e . target as HTMLInputElement ). checked ;
realtimeClient . setMirror ( enabled );
});
// Cleanup on page unload
window . addEventListener ( "beforeunload" , () => {
realtimeClient . disconnect ();
stream . getTracks (). forEach ( track => track . stop ());
});
return realtimeClient ;
} catch ( error ) {
console . error ( "Failed to setup realtime video:" , error );
throw error ;
}
}
// Initialize
setupRealtimeVideo ();
Best Practices
Use model properties for video constraints
Always use the model’s fps
, width
, and height
properties when calling getUserMedia to ensure optimal performance and compatibility. const model = models . realtime ( "mirage" );
const stream = await navigator . mediaDevices . getUserMedia ({
video: {
frameRate: model . fps ,
width: model . width ,
height: model . height ,
},
});
For best results, keep enrich: true
(default) to let Decart’s AI enhance your prompts. Only disable it if you need exact prompt control.
Handle connection state changes
Always listen to connectionChange
events to update your UI and handle reconnection logic gracefully.
Always call disconnect()
and stop media tracks when done to avoid memory leaks and unnecessary resource usage.
Use mirror mode for front cameras
Enable mirror mode when using front-facing cameras to match user expectations from selfie/video call experiences.
API Reference
client.realtime.connect(stream, options)
Connects to the realtime transformation service.
Parameters:
stream: MediaStream
- MediaStream from getUserMedia
options.model: ModelDefinition
- Realtime model from models.realtime()
options.onRemoteStream: (stream: MediaStream) => void
- Callback for transformed video stream
options.initialState.prompt: { text: string; enrich?: boolean }
- Initial transformation prompt
options.initialState.mirror?: boolean
- Enable mirror mode (default: false)
Returns: Promise<RealtimeClient>
- Connected realtime client instance
realtimeClient.setPrompt(prompt, options?)
Changes the transformation style.
Parameters:
prompt: string
- Text description of desired style
options.enrich?: boolean
- Whether to enhance the prompt (default: true)
realtimeClient.setMirror(enabled)
Toggles video mirroring.
Parameters:
enabled: boolean
- Whether to enable mirror mode
realtimeClient.isConnected()
Check if currently connected.
Returns: boolean
realtimeClient.getConnectionState()
Get current connection state.
Returns: "connected" | "connecting" | "disconnected"
realtimeClient.sessionId
The ID of the current realtime inference session.
Type: string
realtimeClient.disconnect()
Closes the connection and cleans up resources.
Events
connectionChange
Fired when connection state changes.
Callback: (state: "connected" | "connecting" | "disconnected") => void
error
Fired when an error occurs.
Callback: (error: DecartSDKError) => void
Next Steps