@robinandeer/rtc-session-components 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/NOTICES ADDED
@@ -0,0 +1,41 @@
1
+ This project includes the following third-party dependencies:
2
+
3
+ ================================================================================
4
+ livekit-client
5
+ https://github.com/livekit/client-sdk-js
6
+ --------------------------------------------------------------------------------
7
+ Licensed under the Apache License, Version 2.0
8
+
9
+ Copyright 2023 LiveKit, Inc.
10
+
11
+ Licensed under the Apache License, Version 2.0 (the "License");
12
+ you may not use this file except in compliance with the License.
13
+ You may obtain a copy of the License at
14
+
15
+ http://www.apache.org/licenses/LICENSE-2.0
16
+
17
+ Unless required by applicable law or agreed to in writing, software
18
+ distributed under the License is distributed on an "AS IS" BASIS,
19
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20
+ See the License for the specific language governing permissions and
21
+ limitations under the License.
22
+
23
+ ================================================================================
24
+ @livekit/components-react
25
+ https://github.com/livekit/components-js
26
+ --------------------------------------------------------------------------------
27
+ Licensed under the Apache License, Version 2.0
28
+
29
+ Copyright 2023 LiveKit, Inc.
30
+
31
+ Licensed under the Apache License, Version 2.0 (the "License");
32
+ you may not use this file except in compliance with the License.
33
+ You may obtain a copy of the License at
34
+
35
+ http://www.apache.org/licenses/LICENSE-2.0
36
+
37
+ Unless required by applicable law or agreed to in writing, software
38
+ distributed under the License is distributed on an "AS IS" BASIS,
39
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
40
+ See the License for the specific language governing permissions and
41
+ limitations under the License.
package/README.md CHANGED
@@ -2,201 +2,317 @@
2
2
 
3
3
  React SDK for real-time AI avatar interactions with GWM-1.
4
4
 
5
+ ## Requirements
6
+
7
+ - React 18+
8
+ - A Runway API secret ([get one here](https://dev.runwayml.com/))
9
+ - A server-side endpoint to create sessions (API secrets must not be exposed to the client)
10
+
5
11
  ## Installation
6
12
 
7
13
  ```bash
8
14
  npm install @runwayml/avatar-react
9
- # or
10
- bun add @runwayml/avatar-react
11
15
  ```
12
16
 
13
17
  ## Quick Start
14
18
 
19
+ Add an avatar call to your app with just a few lines:
20
+
15
21
  ```tsx
16
- import { AvatarProvider, useAvatar, AvatarCanvas } from '@runwayml/avatar-react';
22
+ import { AvatarCall } from '@runwayml/avatar-react';
17
23
 
18
24
  function App() {
19
25
  return (
20
- <AvatarProvider config={{ apiKey: process.env.RUNWAYML_API_KEY! }}>
21
- <AvatarDemo />
22
- </AvatarProvider>
26
+ <AvatarCall
27
+ avatarId="game-host"
28
+ connectUrl="/api/avatar/connect"
29
+ />
23
30
  );
24
31
  }
32
+ ```
25
33
 
26
- function AvatarDemo() {
27
- const { connect, disconnect, speak, isConnected, isConnecting } = useAvatar();
34
+ That's it! The component handles session creation, WebRTC connection, and renders a default UI with the avatar video and controls.
28
35
 
29
- const handleConnect = async () => {
30
- await connect({ modelId: 'gwm-1' });
31
- };
36
+ You can use preset avatars like `game-host`, `coding-teacher`, `language-tutor`, and more. See the [Runway Developer Portal](https://dev.runwayml.com/) for the full list and creating custom avatars.
32
37
 
33
- return (
34
- <div>
35
- <AvatarCanvas fallback={<div>Not connected</div>} showFps />
36
-
37
- {!isConnected ? (
38
- <button onClick={handleConnect} disabled={isConnecting}>
39
- {isConnecting ? 'Connecting...' : 'Connect'}
40
- </button>
41
- ) : (
42
- <>
43
- <button onClick={() => speak('Hello, world!')}>Say Hello</button>
44
- <button onClick={disconnect}>Disconnect</button>
45
- </>
46
- )}
47
- </div>
48
- );
38
+ ### Optional: Add Default Styles
39
+
40
+ Import the optional stylesheet for a polished look out of the box:
41
+
42
+ ```tsx
43
+ import '@runwayml/avatar-react/styles.css';
44
+ ```
45
+
46
+ The styles use CSS custom properties for easy customization:
47
+
48
+ ```css
49
+ :root {
50
+ --avatar-bg: #a78bfa; /* Video background color */
51
+ --avatar-radius: 16px; /* Container border radius */
52
+ --avatar-control-size: 48px; /* Control button size */
53
+ --avatar-end-call-bg: #ef4444; /* End call button color */
49
54
  }
50
55
  ```
51
56
 
52
- ## API Reference
57
+ See [`examples/`](./examples) for complete working examples:
58
+ - [`nextjs`](./examples/nextjs) - Next.js App Router
59
+ - [`react-router`](./examples/react-router) - React Router v7 framework mode
60
+ - [`express`](./examples/express) - Express + Vite
53
61
 
54
- ### Components
62
+ ## How It Works
55
63
 
56
- #### `<AvatarProvider>`
64
+ 1. **Client** calls your server endpoint with the `avatarId`
65
+ 2. **Server** uses your Runway API secret to create a session via `@runwayml/sdk`
66
+ 3. **Server** returns connection credentials (token, URL) to the client
67
+ 4. **Client** establishes a WebRTC connection for real-time video/audio
57
68
 
58
- Provides the avatar client context to child components.
69
+ This flow keeps your API secret secure on the server while enabling low-latency communication.
59
70
 
60
- ```tsx
61
- <AvatarProvider config={{ apiKey: 'your-api-key', debug: true }}>{children}</AvatarProvider>
62
- ```
71
+ ## Server Setup
63
72
 
64
- **Props:**
73
+ Your server endpoint receives the `avatarId` and returns session credentials. Use `@runwayml/sdk` to create the session:
65
74
 
66
- - `config.apiKey` (required) - Your RunwayML API key
67
- - `config.baseUrl` - Custom API base URL
68
- - `config.wsUrl` - Custom WebSocket URL
69
- - `config.timeout` - Request timeout in ms (default: 30000)
70
- - `config.maxRetries` - Max retry attempts (default: 2)
71
- - `config.debug` - Enable debug logging (default: false)
75
+ ```ts
76
+ // /api/avatar/connect (Next.js App Router example)
77
+ import Runway from '@runwayml/sdk';
72
78
 
73
- #### `<AvatarCanvas>`
79
+ const runway = new Runway(); // Uses RUNWAYML_API_SECRET env var
74
80
 
75
- Canvas component that automatically renders avatar video frames.
81
+ export async function POST(req: Request) {
82
+ const { avatarId } = await req.json();
76
83
 
77
- ```tsx
78
- <AvatarCanvas showFps targetFps={30} fallback={<Placeholder />} style={{ width: '100%' }} />
84
+ const session = await runway.realtime.sessions.create({
85
+ model: 'calliope',
86
+ options: { avatar: avatarId },
87
+ });
88
+
89
+ return Response.json({
90
+ sessionId: session.id,
91
+ serverUrl: session.url,
92
+ token: session.token,
93
+ roomName: session.room_name,
94
+ });
95
+ }
79
96
  ```
80
97
 
81
- ### Hooks
98
+ ## Customization
82
99
 
83
- #### `useAvatar(options?)`
100
+ ### Custom Connect Function
84
101
 
85
- Main hook for managing avatar sessions.
102
+ For more control over the connection flow:
86
103
 
87
104
  ```tsx
88
- const {
89
- session,
90
- connectionState,
91
- avatarState,
92
- isConnected,
93
- isConnecting,
94
- error,
95
- connect,
96
- disconnect,
97
- speak,
98
- sendAudio,
99
- setEmotion,
100
- setGaze,
101
- interrupt,
102
- } = useAvatar({
103
- autoConnect: false,
104
- sessionConfig: { modelId: 'gwm-1' },
105
- onConnected: () => console.log('Connected!'),
106
- onError: (error) => console.error(error),
107
- });
108
- ```
109
-
110
- #### `useAvatarVideo(options?)`
111
-
112
- Hook for handling avatar video frames.
105
+ <AvatarCall
106
+ avatarId="game-host"
107
+ connect={async (avatarId) => {
108
+ const res = await fetch('/api/avatar/connect', {
109
+ method: 'POST',
110
+ headers: {
111
+ 'Authorization': `Bearer ${token}`,
112
+ 'Content-Type': 'application/json',
113
+ },
114
+ body: JSON.stringify({ avatarId }),
115
+ });
116
+ return res.json();
117
+ }}
118
+ />
119
+ ```
120
+
121
+ ### Custom UI with Child Components
122
+
123
+ Use the built-in components for custom layouts:
113
124
 
114
125
  ```tsx
115
- const { canvasRef, dimensions, fps, isPlaying, frameCount } = useAvatarVideo({
116
- targetFps: 30,
117
- autoPlay: true,
118
- onFrame: (frame) => console.log('Frame received'),
119
- });
126
+ import { AvatarCall, AvatarVideo, ControlBar, UserVideo } from '@runwayml/avatar-react';
127
+
128
+ <AvatarCall avatarId="game-host" connectUrl="/api/avatar/connect">
129
+ <div className="call-layout">
130
+ <AvatarVideo className="avatar" />
131
+ <UserVideo className="self-view" />
132
+ <ControlBar className="controls" />
133
+ </div>
134
+ </AvatarCall>
120
135
  ```
121
136
 
122
- #### `useAvatarAudio(options?)`
137
+ ### Render Props
123
138
 
124
- Hook for handling avatar audio output.
139
+ All components support render props for complete control:
125
140
 
126
141
  ```tsx
127
- const { isPlaying, isMuted, volume, setVolume, toggleMute } = useAvatarAudio({
128
- autoPlay: true,
129
- volume: 1,
130
- });
142
+ <AvatarVideo>
143
+ {({ hasVideo, isConnecting, isSpeaking, trackRef }) => (
144
+ <div className={isSpeaking ? 'speaking' : ''}>
145
+ {isConnecting && <Spinner />}
146
+ {hasVideo && <VideoTrack trackRef={trackRef} />}
147
+ </div>
148
+ )}
149
+ </AvatarVideo>
131
150
  ```
132
151
 
133
- #### `useAvatarMicrophone(options?)`
152
+ ### CSS Styling with Data Attributes
134
153
 
135
- Hook for capturing and sending microphone audio.
154
+ Style connection states with CSS:
136
155
 
137
156
  ```tsx
138
- const { isActive, hasPermission, audioLevel, start, stop, toggleMute } = useAvatarMicrophone({
139
- sampleRate: 48000,
140
- echoCancellation: true,
141
- noiseSuppression: true,
142
- });
157
+ <AvatarCall avatarId="game-host" connectUrl="/api/avatar/connect" className="my-avatar" />
143
158
  ```
144
159
 
145
- ### Types
160
+ ```css
161
+ .my-avatar[data-state="connecting"] {
162
+ opacity: 0.5;
163
+ }
146
164
 
147
- ```typescript
148
- interface AvatarSessionConfig {
149
- modelId: string;
150
- resolution?: { width: number; height: number };
151
- audio?: AudioConfig;
152
- initialState?: Partial<AvatarState>;
165
+ .my-avatar[data-state="error"] {
166
+ border: 2px solid red;
153
167
  }
154
168
 
155
- interface AvatarState {
156
- emotion: 'neutral' | 'happy' | 'sad' | 'surprised' | 'angry' | 'confused' | 'thinking';
157
- isSpeaking: boolean;
158
- isListening: boolean;
159
- gazeDirection: { x: number; y: number };
169
+ .my-avatar[data-state="connected"] {
170
+ border: 2px solid green;
160
171
  }
172
+ ```
161
173
 
162
- type ConnectionState = 'disconnected' | 'connecting' | 'connected' | 'reconnecting' | 'error';
174
+ ## Callbacks
175
+
176
+ ```tsx
177
+ <AvatarCall
178
+ avatarId="game-host"
179
+ connectUrl="/api/avatar/connect"
180
+ onEnd={() => console.log('Call ended')}
181
+ onError={(error) => console.error('Error:', error)}
182
+ />
163
183
  ```
164
184
 
165
- ## Direct Client Usage
185
+ ## Hooks
166
186
 
167
- For advanced use cases, you can use the client directly:
187
+ Use hooks for custom components within an `AvatarCall` or `AvatarSession`:
168
188
 
169
- ```typescript
170
- import { createAvatarClient } from '@runwayml/avatar-react';
189
+ ### useAvatarSession
171
190
 
172
- const client = createAvatarClient({ apiKey: 'your-api-key' });
191
+ Access session state and controls:
173
192
 
174
- await client.createSession({ modelId: 'gwm-1' });
193
+ ```tsx
194
+ function MyComponent() {
195
+ const { state, sessionId, error, end } = useAvatarSession();
175
196
 
176
- client.on('videoFrame', (event) => {
177
- // Handle video frame
178
- });
197
+ if (state === 'connecting') return <Loading />;
198
+ if (state === 'error') return <Error message={error.message} />;
179
199
 
180
- client.speak('Hello!');
181
- client.disconnect();
200
+ return <button onClick={end}>End Call</button>;
201
+ }
182
202
  ```
183
203
 
184
- ## Development
204
+ ### useAvatar
185
205
 
186
- ```bash
187
- # Install dependencies
188
- bun install
206
+ Access the remote avatar's video/audio:
189
207
 
190
- # Build
191
- bun run build
208
+ ```tsx
209
+ function CustomAvatar() {
210
+ const { videoTrackRef, isSpeaking, hasVideo, hasAudio } = useAvatar();
192
211
 
193
- # Type check
194
- bun run typecheck
212
+ return (
213
+ <div data-speaking={isSpeaking}>
214
+ {hasVideo && <VideoTrack trackRef={videoTrackRef} />}
215
+ </div>
216
+ );
217
+ }
218
+ ```
219
+
220
+ ### useLocalMedia
221
+
222
+ Control local camera and microphone:
223
+
224
+ ```tsx
225
+ function MediaControls() {
226
+ const {
227
+ isMicEnabled,
228
+ isCameraEnabled,
229
+ toggleMic,
230
+ toggleCamera,
231
+ toggleScreenShare,
232
+ } = useLocalMedia();
233
+
234
+ return (
235
+ <div>
236
+ <button onClick={toggleMic}>{isMicEnabled ? 'Mute' : 'Unmute'}</button>
237
+ <button onClick={toggleCamera}>{isCameraEnabled ? 'Hide' : 'Show'}</button>
238
+ </div>
239
+ );
240
+ }
241
+ ```
242
+
243
+ ## Advanced: AvatarSession
244
+
245
+ For full control over session management, use `AvatarSession` directly with pre-fetched credentials:
246
+
247
+ ```tsx
248
+ import { AvatarSession, AvatarVideo, ControlBar } from '@runwayml/avatar-react';
249
+
250
+ function AdvancedUsage({ credentials }) {
251
+ return (
252
+ <AvatarSession
253
+ credentials={credentials}
254
+ audio={true}
255
+ video={true}
256
+ onEnd={() => console.log('Ended')}
257
+ onError={(err) => console.error(err)}
258
+ >
259
+ <AvatarVideo />
260
+ <ControlBar />
261
+ </AvatarSession>
262
+ );
263
+ }
264
+ ```
265
+
266
+ ## Components Reference
195
267
 
196
- # Run tests
197
- bun test
268
+ | Component | Description |
269
+ |-----------|-------------|
270
+ | `AvatarCall` | High-level component that handles session creation |
271
+ | `AvatarSession` | Low-level wrapper that requires credentials |
272
+ | `AvatarVideo` | Renders the remote avatar video |
273
+ | `UserVideo` | Renders the local user's camera |
274
+ | `ControlBar` | Media control buttons (mic, camera, end call) |
275
+ | `ScreenShareVideo` | Renders screen share content |
276
+ | `AudioRenderer` | Handles avatar audio playback |
277
+
278
+ ## TypeScript
279
+
280
+ All components and hooks are fully typed:
281
+
282
+ ```tsx
283
+ import type {
284
+ AvatarCallProps,
285
+ SessionCredentials,
286
+ SessionState,
287
+ } from '@runwayml/avatar-react';
198
288
  ```
199
289
 
290
+ ## Browser Support
291
+
292
+ This SDK uses WebRTC for real-time communication. Supported browsers:
293
+
294
+ - Chrome 74+
295
+ - Firefox 78+
296
+ - Safari 14.1+
297
+ - Edge 79+
298
+
299
+ Users must grant camera and microphone permissions when prompted.
300
+
301
+ ## Troubleshooting
302
+
303
+ **"Failed to connect" or timeout errors**
304
+ - Verify your server endpoint is returning the correct credential format
305
+ - Check that `RUNWAYML_API_SECRET` is set correctly on your server
306
+
307
+ **No video/audio**
308
+ - Ensure the user has granted camera/microphone permissions
309
+ - Check browser console for WebRTC errors
310
+ - Verify the device has a working camera/microphone
311
+
312
+ **CORS errors**
313
+ - Your server endpoint must accept requests from your client's origin
314
+ - For local development, ensure both client and server are on compatible origins
315
+
200
316
  ## License
201
317
 
202
318
  MIT