@reactor-models/h3-reference-to-video-turbo-realtime 0.3.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/README.md ADDED
@@ -0,0 +1,1401 @@
1
+ # @reactor-models/h3-reference-to-video-turbo-realtime
2
+
3
+ > Typed JavaScript + React SDK for the **H3ReferenceToVideoTurboRealtime** model on [Reactor](https://reactor.inc). Version **v0.3.0**.
4
+
5
+ ---
6
+
7
+ ## Get started
8
+
9
+ Scaffold a starter app for **H3ReferenceToVideoTurboRealtime** with [`create-reactor-app`](https://www.npmjs.com/package/create-reactor-app):
10
+
11
+ ```shell
12
+ npx create-reactor-app my-app --model=h3-reference-to-video-turbo-realtime
13
+ ```
14
+
15
+ ```shell
16
+ pnpm dlx create-reactor-app my-app --model=h3-reference-to-video-turbo-realtime
17
+ ```
18
+
19
+ ---
20
+
21
+ ## Install
22
+
23
+ ```shell
24
+ npm install @reactor-models/h3-reference-to-video-turbo-realtime
25
+ ```
26
+
27
+ ```shell
28
+ pnpm add @reactor-models/h3-reference-to-video-turbo-realtime
29
+ ```
30
+
31
+ The package exports a plain-JavaScript client and a set of React bindings. Import whichever you need from `@reactor-models/h3-reference-to-video-turbo-realtime`:
32
+
33
+ ```typescript
34
+ import { H3ReferenceToVideoTurboRealtimeModel } from "@reactor-models/h3-reference-to-video-turbo-realtime";
35
+ ```
36
+
37
+ ```typescript
38
+ import { H3ReferenceToVideoTurboRealtimeProvider, useH3ReferenceToVideoTurboRealtime } from "@reactor-models/h3-reference-to-video-turbo-realtime";
39
+ ```
40
+
41
+ React 18 or later is required when using the provider and hooks. The token-loading examples below use [React 19's `use()`](https://react.dev/reference/react/use); on React 18, fetch the JWT in a `useEffect` and pass it to the provider once it resolves.
42
+
43
+ ---
44
+
45
+ ## Authenticate
46
+
47
+ Reactor uses short-lived JWTs for session auth. You hold your API key on your server, mint a token on demand, and the client never sees the raw key. Tokens are valid for **6 hours** — if one leaks, it expires on its own.
48
+
49
+ Mint a JWT with **`POST https://api.reactor.inc/tokens`** and the **`Reactor-API-Key`** header; the response JSON is `{ "jwt": "..." }`.
50
+
51
+ ### JavaScript (Next.js route handler)
52
+
53
+ ```typescript
54
+ // app/api/reactor/token/route.ts
55
+ import { NextResponse } from "next/server";
56
+
57
+ export async function POST() {
58
+ const res = await fetch("https://api.reactor.inc/tokens", {
59
+ method: "POST",
60
+ headers: { "Reactor-API-Key": process.env.REACTOR_API_KEY! },
61
+ });
62
+ const { jwt } = await res.json();
63
+ return NextResponse.json({ jwt });
64
+ }
65
+ ```
66
+
67
+ ### React (provider)
68
+
69
+ Call the `/api/reactor/token` route above from a client component and pass the result to the provider:
70
+
71
+ ```tsx
72
+ "use client";
73
+
74
+ import { use } from "react";
75
+ import { H3ReferenceToVideoTurboRealtimeProvider } from "@reactor-models/h3-reference-to-video-turbo-realtime";
76
+ import { ReactorView } from "@reactor-team/js-sdk";
77
+
78
+ async function getToken() {
79
+ const r = await fetch("/api/reactor/token", { method: "POST" });
80
+ const { jwt } = await r.json();
81
+ return jwt;
82
+ }
83
+
84
+ const tokenPromise = getToken();
85
+
86
+ export default function App() {
87
+ const token = use(tokenPromise);
88
+ return (
89
+ <H3ReferenceToVideoTurboRealtimeProvider jwtToken={token} connectOptions={{ autoConnect: true }}>
90
+ <ReactorView className="w-full aspect-video" />
91
+ </H3ReferenceToVideoTurboRealtimeProvider>
92
+ );
93
+ }
94
+ ```
95
+
96
+ ---
97
+
98
+ ## Connect
99
+
100
+ ### JavaScript
101
+
102
+ ```typescript
103
+ import { H3ReferenceToVideoTurboRealtimeModel } from "@reactor-models/h3-reference-to-video-turbo-realtime";
104
+
105
+ const h3ReferenceToVideoTurboRealtime = new H3ReferenceToVideoTurboRealtimeModel();
106
+ await h3ReferenceToVideoTurboRealtime.connect(jwt);
107
+ ```
108
+
109
+ ### React
110
+
111
+ The provider takes the JWT as a prop; fetch it from the same `/api/reactor/token` route the Authenticate example mints:
112
+
113
+ ```tsx
114
+ "use client";
115
+
116
+ import { use } from "react";
117
+ import { H3ReferenceToVideoTurboRealtimeProvider, useH3ReferenceToVideoTurboRealtime } from "@reactor-models/h3-reference-to-video-turbo-realtime";
118
+
119
+ async function getToken() {
120
+ const r = await fetch("/api/reactor/token", { method: "POST" });
121
+ const { jwt } = await r.json();
122
+ return jwt;
123
+ }
124
+
125
+ const tokenPromise = getToken();
126
+
127
+ function Controller() {
128
+ const { status } = useH3ReferenceToVideoTurboRealtime();
129
+ return <span>Status: {status}</span>;
130
+ }
131
+
132
+ export default function App() {
133
+ const token = use(tokenPromise);
134
+ return (
135
+ <H3ReferenceToVideoTurboRealtimeProvider jwtToken={token}>
136
+ <Controller />
137
+ </H3ReferenceToVideoTurboRealtimeProvider>
138
+ );
139
+ }
140
+ ```
141
+
142
+ ---
143
+
144
+ ## Events
145
+
146
+ Client-to-model commands. The typed surface is `H3ReferenceToVideoTurboRealtimeModel` (one method per event) in plain JS, and `useH3ReferenceToVideoTurboRealtime()` in React — every field name below matches the parameter name the method accepts. Every awaited call resolves once the model's handler has completed; a command whose handler returns a message resolves with that reply (see each event's "Returns" line).
147
+
148
+ ### `pop`
149
+
150
+ Remove a clip from its queue; reply clip_popped and emit queue_update and state_update. A running build finishes but is discarded. Refuses with command_error for a missing/unknown id. Use stop for the playing clip.
151
+
152
+ Returns: [`clip_popped`](#clip_popped) — `{ type: "clip_popped", clip: null }` (or `undefined` when the send fails).
153
+
154
+ | Parameter | Type | Description |
155
+ |---|---|---|
156
+ | `clip_id` | `string` | UUID of the clip to remove from either queue. _(default `""`)_ |
157
+
158
+ #### JavaScript
159
+
160
+ ```typescript
161
+ import { H3ReferenceToVideoTurboRealtimeModel } from "@reactor-models/h3-reference-to-video-turbo-realtime";
162
+
163
+ const h3ReferenceToVideoTurboRealtime = new H3ReferenceToVideoTurboRealtimeModel();
164
+ await h3ReferenceToVideoTurboRealtime.connect(jwt);
165
+
166
+ const reply = await h3ReferenceToVideoTurboRealtime.pop({ clip_id: "" });
167
+
168
+ if (reply) {
169
+ console.log("clip_popped", reply.clip);
170
+ }
171
+ ```
172
+
173
+ #### React
174
+
175
+ ```tsx
176
+ "use client";
177
+ import { useH3ReferenceToVideoTurboRealtime } from "@reactor-models/h3-reference-to-video-turbo-realtime";
178
+
179
+ function Example() {
180
+ const { pop } = useH3ReferenceToVideoTurboRealtime();
181
+
182
+ return <button onClick={() => pop({ clip_id: "" })}>pop</button>;
183
+ }
184
+ ```
185
+
186
+ ### `move`
187
+
188
+ Reposition within the current queue, 0 = front. A running build is unaffected. Replies clip_moved and emits queue_update; refuses with command_error for a missing/unknown id or invalid position.
189
+
190
+ Returns: [`clip_moved`](#clip_moved) — `{ type: "clip_moved", clip: null, queue: "", position: 0 }` (or `undefined` when the send fails).
191
+
192
+ | Parameter | Type | Description |
193
+ |---|---|---|
194
+ | `clip_id` | `string` | UUID of a queued clip. _(default `""`)_ |
195
+ | `position` | `number` | Position in its current queue; past the end appends. _(min 0, default `0`)_ |
196
+
197
+ #### JavaScript
198
+
199
+ ```typescript
200
+ import { H3ReferenceToVideoTurboRealtimeModel } from "@reactor-models/h3-reference-to-video-turbo-realtime";
201
+
202
+ const h3ReferenceToVideoTurboRealtime = new H3ReferenceToVideoTurboRealtimeModel();
203
+ await h3ReferenceToVideoTurboRealtime.connect(jwt);
204
+
205
+ const reply = await h3ReferenceToVideoTurboRealtime.move({ clip_id: "", position: 0 });
206
+
207
+ if (reply) {
208
+ console.log(
209
+ "clip_moved",
210
+ reply.clip,
211
+ reply.queue,
212
+ reply.position,
213
+ );
214
+ }
215
+ ```
216
+
217
+ #### React
218
+
219
+ ```tsx
220
+ "use client";
221
+ import { useH3ReferenceToVideoTurboRealtime } from "@reactor-models/h3-reference-to-video-turbo-realtime";
222
+
223
+ function Example() {
224
+ const { move } = useH3ReferenceToVideoTurboRealtime();
225
+
226
+ return <button onClick={() => move({ clip_id: "", position: 0 })}>move</button>;
227
+ }
228
+ ```
229
+
230
+ ### `play`
231
+
232
+ Play the front ready clip, or a named ready clip. Consumes it and emits clip_started, queue_update and state_update. Refuses with command_error if already playing or no matching ready clip exists.
233
+
234
+ Returns: nothing — the awaited call resolves `undefined` once the model's handler has run.
235
+
236
+ | Parameter | Type | Description |
237
+ |---|---|---|
238
+ | `clip_id` | `string` | UUID of a ready clip; blank takes the playout front. _(default `""`)_ |
239
+
240
+ #### JavaScript
241
+
242
+ ```typescript
243
+ import { H3ReferenceToVideoTurboRealtimeModel } from "@reactor-models/h3-reference-to-video-turbo-realtime";
244
+
245
+ const h3ReferenceToVideoTurboRealtime = new H3ReferenceToVideoTurboRealtimeModel();
246
+ await h3ReferenceToVideoTurboRealtime.connect(jwt);
247
+
248
+ await h3ReferenceToVideoTurboRealtime.play({ clip_id: "" });
249
+ ```
250
+
251
+ #### React
252
+
253
+ ```tsx
254
+ "use client";
255
+ import { useH3ReferenceToVideoTurboRealtime } from "@reactor-models/h3-reference-to-video-turbo-realtime";
256
+
257
+ function Example() {
258
+ const { play } = useH3ReferenceToVideoTurboRealtime();
259
+
260
+ return <button onClick={() => play({ clip_id: "" })}>play</button>;
261
+ }
262
+ ```
263
+
264
+ ### `stop`
265
+
266
+ Cut playback and emit clip_stopped and state_update; queues are untouched. Under autoplay this skips to the next clip. Refuses with command_error when nothing is playing.
267
+
268
+ Returns: nothing — the awaited call resolves `undefined` once the model's handler has run.
269
+
270
+ _No parameters._
271
+
272
+ #### JavaScript
273
+
274
+ ```typescript
275
+ import { H3ReferenceToVideoTurboRealtimeModel } from "@reactor-models/h3-reference-to-video-turbo-realtime";
276
+
277
+ const h3ReferenceToVideoTurboRealtime = new H3ReferenceToVideoTurboRealtimeModel();
278
+ await h3ReferenceToVideoTurboRealtime.connect(jwt);
279
+
280
+ await h3ReferenceToVideoTurboRealtime.stop();
281
+ ```
282
+
283
+ #### React
284
+
285
+ ```tsx
286
+ "use client";
287
+ import { useH3ReferenceToVideoTurboRealtime } from "@reactor-models/h3-reference-to-video-turbo-realtime";
288
+
289
+ function Example() {
290
+ const { stop } = useH3ReferenceToVideoTurboRealtime();
291
+
292
+ return <button onClick={() => stop()}>stop</button>;
293
+ }
294
+ ```
295
+
296
+ ### `reset`
297
+
298
+ Drop both queues, cut playback, restore defaults and clear tracks. Replies session_reset; emits queue_update, state_update and clip_stopped if playing. In-flight results are discarded.
299
+
300
+ Returns: [`session_reset`](#session_reset) — `{ type: "session_reset", was_playing: true, cleared_clips: 0 }` (or `undefined` when the send fails).
301
+
302
+ _No parameters._
303
+
304
+ #### JavaScript
305
+
306
+ ```typescript
307
+ import { H3ReferenceToVideoTurboRealtimeModel } from "@reactor-models/h3-reference-to-video-turbo-realtime";
308
+
309
+ const h3ReferenceToVideoTurboRealtime = new H3ReferenceToVideoTurboRealtimeModel();
310
+ await h3ReferenceToVideoTurboRealtime.connect(jwt);
311
+
312
+ const reply = await h3ReferenceToVideoTurboRealtime.reset();
313
+
314
+ if (reply) {
315
+ console.log("session_reset", reply.was_playing, reply.cleared_clips);
316
+ }
317
+ ```
318
+
319
+ #### React
320
+
321
+ ```tsx
322
+ "use client";
323
+ import { useH3ReferenceToVideoTurboRealtime } from "@reactor-models/h3-reference-to-video-turbo-realtime";
324
+
325
+ function Example() {
326
+ const { reset } = useH3ReferenceToVideoTurboRealtime();
327
+
328
+ return <button onClick={() => reset()}>reset</button>;
329
+ }
330
+ ```
331
+
332
+ ### `enqueue`
333
+
334
+ Queue a clip with 1–9 reference images at position, or at the back. Each clip keeps its own prompt and ordered references: new images do not alter earlier requests. The images guide appearance, not a fixed first or last frame. Replies clip_queued and broadcasts queue_update and state_update; completion emits clip_generated. Refuses with command_error for empty text, missing/invalid reference, invalid parameters or a full generation queue.
335
+
336
+ Returns: [`clip_queued`](#clip_queued) — `{ type: "clip_queued", clip: null }` (or `undefined` when the send fails).
337
+
338
+ | Parameter | Type | Description |
339
+ |---|---|---|
340
+ | `seed` | `number \| null` | Omitted or null uses and advances the default seed. An explicit seed leaves the default unchanged. _(min 0, default `null`)_ |
341
+ | `prompt` | `string` | What the clip should show and sound like; stored unchanged and never truncated. A prompt past the model's text budget fails the clip with clip_failed instead. _(default `""`)_ |
342
+ | `seconds` | `number \| null` | Requested duration between 5 and 15.084 seconds, rounded to frames then aligned upward to a supported length. Actual output ranges from 5.167 to 15.083 seconds. Null uses the session default. _(min 5, max 15.084, default `null`)_ |
343
+ | `metadata` | `string` | Opaque client string, up to 2000 characters, echoed unchanged with the clip. _(maxLength 2000, default `""`)_ |
344
+ | `position` | `number \| null` | Generation queue position: 0 is the next build; omitted or past the end appends. The running build is unaffected. _(min 0, default `null`)_ |
345
+ | `reference_image` | `FileRef \| null` | One JPEG, PNG or WebP reference for this clip. Guides subjects and appearance, not a keyframe. Supply this or reference_images, not both. _(default `null`)_ |
346
+ | `reference_images` | `FileRef[] \| null` | Ordered list of 1–9 uploaded JPEG, PNG or WebP references. In the prompt, Picture 1 names the first image, Picture 2 the second, and so on. Supply this or reference_image, not both. Each clip retains its own list, including while other clips build or play. _(default `null`)_ |
347
+ | `continue_from_clip_id` | `string` | UUID of a generated clip to continue temporally from: this clip's motion, camera and audio carry across the boundary while its own references still drive appearance. Blank for an independent clip. The source must have finished generating; unknown or dropped ids fall back to an independent clip. _(default `""`)_ |
348
+
349
+ #### JavaScript
350
+
351
+ ```typescript
352
+ import { H3ReferenceToVideoTurboRealtimeModel } from "@reactor-models/h3-reference-to-video-turbo-realtime";
353
+
354
+ const h3ReferenceToVideoTurboRealtime = new H3ReferenceToVideoTurboRealtimeModel();
355
+ await h3ReferenceToVideoTurboRealtime.connect(jwt);
356
+
357
+ const fileRef = await h3ReferenceToVideoTurboRealtime.uploadFile(blob);
358
+ const reply = await h3ReferenceToVideoTurboRealtime.enqueue({ reference_image: fileRef, seed: null, prompt: "A sunset over the ocean", seconds: null, metadata: "", position: null, reference_images: null, continue_from_clip_id: "" });
359
+
360
+ if (reply) {
361
+ console.log("clip_queued", reply.clip);
362
+ }
363
+ ```
364
+
365
+ #### React
366
+
367
+ ```tsx
368
+ "use client";
369
+ import { useH3ReferenceToVideoTurboRealtime } from "@reactor-models/h3-reference-to-video-turbo-realtime";
370
+
371
+ function Example() {
372
+ const { enqueue, uploadFile } = useH3ReferenceToVideoTurboRealtime();
373
+
374
+ async function handlePick(file: File) {
375
+ const ref = await uploadFile(file);
376
+ await enqueue({ reference_image: ref, seed: null, prompt: "A sunset over the ocean", seconds: null, metadata: "", position: null, reference_images: null, continue_from_clip_id: "" });
377
+ }
378
+
379
+ return <input type="file" onChange={(e) => handlePick(e.target.files![0])} />;
380
+ }
381
+ ```
382
+
383
+ ### `setSeed`
384
+
385
+ Set the advancing default seed for future enqueues. Replies seed_accepted and emits state_update; an invalid seed emits command_error. Explicit per-clip seeds do not advance it.
386
+
387
+ Returns: [`seed_accepted`](#seed_accepted) — `{ type: "seed_accepted", seed: 0 }` (or `undefined` when the send fails).
388
+
389
+ | Parameter | Type | Description |
390
+ |---|---|---|
391
+ | `seed` | `number` | Nonnegative default seed; queued clips keep their seeds. _(min 0, default `1000`)_ |
392
+
393
+ #### JavaScript
394
+
395
+ ```typescript
396
+ import { H3ReferenceToVideoTurboRealtimeModel } from "@reactor-models/h3-reference-to-video-turbo-realtime";
397
+
398
+ const h3ReferenceToVideoTurboRealtime = new H3ReferenceToVideoTurboRealtimeModel();
399
+ await h3ReferenceToVideoTurboRealtime.connect(jwt);
400
+
401
+ const reply = await h3ReferenceToVideoTurboRealtime.setSeed({ seed: 1000 });
402
+
403
+ if (reply) {
404
+ console.log("seed_accepted", reply.seed);
405
+ }
406
+ ```
407
+
408
+ #### React
409
+
410
+ ```tsx
411
+ "use client";
412
+ import { useH3ReferenceToVideoTurboRealtime } from "@reactor-models/h3-reference-to-video-turbo-realtime";
413
+
414
+ function Example() {
415
+ const { setSeed } = useH3ReferenceToVideoTurboRealtime();
416
+
417
+ return <button onClick={() => setSeed({ seed: 1000 })}>setSeed</button>;
418
+ }
419
+ ```
420
+
421
+ ### `getQueue`
422
+
423
+ Reply queue_update with both queues in full. The history field is always empty: a played clip is not listed and cannot be replayed, though a generated clip can still seed a new one through `enqueue.continue_from_clip_id`. Valid at any time.
424
+
425
+ Returns: [`queue_update`](#queue_update) — `{ type: "queue_update", history: null, playout: null, generation: null }` (or `undefined` when the send fails).
426
+
427
+ _No parameters._
428
+
429
+ #### JavaScript
430
+
431
+ ```typescript
432
+ import { H3ReferenceToVideoTurboRealtimeModel } from "@reactor-models/h3-reference-to-video-turbo-realtime";
433
+
434
+ const h3ReferenceToVideoTurboRealtime = new H3ReferenceToVideoTurboRealtimeModel();
435
+ await h3ReferenceToVideoTurboRealtime.connect(jwt);
436
+
437
+ const reply = await h3ReferenceToVideoTurboRealtime.getQueue();
438
+
439
+ if (reply) {
440
+ console.log(
441
+ "queue_update",
442
+ reply.history,
443
+ reply.playout,
444
+ reply.generation,
445
+ );
446
+ }
447
+ ```
448
+
449
+ #### React
450
+
451
+ ```tsx
452
+ "use client";
453
+ import { useH3ReferenceToVideoTurboRealtime } from "@reactor-models/h3-reference-to-video-turbo-realtime";
454
+
455
+ function Example() {
456
+ const { getQueue } = useH3ReferenceToVideoTurboRealtime();
457
+
458
+ return <button onClick={() => getQueue()}>getQueue</button>;
459
+ }
460
+ ```
461
+
462
+ ### `getState`
463
+
464
+ Reply state_update with defaults, capacities, playback progress and valid_commands. Valid at any time.
465
+
466
+ Returns: [`state_update`](#state_update) — `{ type: "state_update", seed: 0, width: 0, aspect: "", height: 0, playing: true, autoplay: true, clip_seconds: 0, clips_played: 0, seconds_sent: 0, playout_queued: 0, valid_commands: null, playing_clip_id: null, clip_seconds_max: 0, clip_seconds_min: 0, playout_capacity: 0, flush_on_clip_end: true, generation_queued: 0, generation_capacity: 0 }` (or `undefined` when the send fails).
467
+
468
+ _No parameters._
469
+
470
+ #### JavaScript
471
+
472
+ ```typescript
473
+ import { H3ReferenceToVideoTurboRealtimeModel } from "@reactor-models/h3-reference-to-video-turbo-realtime";
474
+
475
+ const h3ReferenceToVideoTurboRealtime = new H3ReferenceToVideoTurboRealtimeModel();
476
+ await h3ReferenceToVideoTurboRealtime.connect(jwt);
477
+
478
+ const reply = await h3ReferenceToVideoTurboRealtime.getState();
479
+
480
+ if (reply) {
481
+ console.log(
482
+ "state_update",
483
+ reply.seed,
484
+ reply.width,
485
+ reply.aspect,
486
+ reply.height,
487
+ reply.playing,
488
+ reply.autoplay,
489
+ reply.clip_seconds,
490
+ reply.clips_played,
491
+ reply.seconds_sent,
492
+ reply.playout_queued,
493
+ reply.valid_commands,
494
+ reply.playing_clip_id,
495
+ reply.clip_seconds_max,
496
+ reply.clip_seconds_min,
497
+ reply.playout_capacity,
498
+ reply.flush_on_clip_end,
499
+ reply.generation_queued,
500
+ reply.generation_capacity,
501
+ );
502
+ }
503
+ ```
504
+
505
+ #### React
506
+
507
+ ```tsx
508
+ "use client";
509
+ import { useH3ReferenceToVideoTurboRealtime } from "@reactor-models/h3-reference-to-video-turbo-realtime";
510
+
511
+ function Example() {
512
+ const { getState } = useH3ReferenceToVideoTurboRealtime();
513
+
514
+ return <button onClick={() => getState()}>getState</button>;
515
+ }
516
+ ```
517
+
518
+ ### `setCanvas`
519
+
520
+ Set the canvas while both queues and playback are empty. Replies canvas_accepted and emits state_update; refuses with command_error for an unsupported aspect or nonempty session.
521
+
522
+ Returns: [`canvas_accepted`](#canvas_accepted) — `{ type: "canvas_accepted", width: 0, aspect: "", height: 0 }` (or `undefined` when the send fails).
523
+
524
+ | Parameter | Type | Description |
525
+ |---|---|---|
526
+ | `aspect` | `"16:9" \| "1:1" \| "9:16" \| "4:3"` | Choose 16:9 (1344x768), 1:1 (768x768), 9:16 (768x1344), or 4:3 (1024x768). _(default `"16:9"`)_ |
527
+
528
+ #### JavaScript
529
+
530
+ ```typescript
531
+ import { H3ReferenceToVideoTurboRealtimeModel } from "@reactor-models/h3-reference-to-video-turbo-realtime";
532
+
533
+ const h3ReferenceToVideoTurboRealtime = new H3ReferenceToVideoTurboRealtimeModel();
534
+ await h3ReferenceToVideoTurboRealtime.connect(jwt);
535
+
536
+ const reply = await h3ReferenceToVideoTurboRealtime.setCanvas({ aspect: "16:9" });
537
+
538
+ if (reply) {
539
+ console.log(
540
+ "canvas_accepted",
541
+ reply.width,
542
+ reply.aspect,
543
+ reply.height,
544
+ );
545
+ }
546
+ ```
547
+
548
+ #### React
549
+
550
+ ```tsx
551
+ "use client";
552
+ import { useH3ReferenceToVideoTurboRealtime } from "@reactor-models/h3-reference-to-video-turbo-realtime";
553
+
554
+ function Example() {
555
+ const { setCanvas } = useH3ReferenceToVideoTurboRealtime();
556
+
557
+ return <button onClick={() => setCanvas({ aspect: "16:9" })}>setCanvas</button>;
558
+ }
559
+ ```
560
+
561
+ ### `setAutoplay`
562
+
563
+ Automatically play ready clips whenever idle. Empty queues wait; no prompts are generated automatically. Replies autoplay_accepted and emits state_update.
564
+
565
+ Returns: [`autoplay_accepted`](#autoplay_accepted) — `{ type: "autoplay_accepted", enabled: true }` (or `undefined` when the send fails).
566
+
567
+ | Parameter | Type | Description |
568
+ |---|---|---|
569
+ | `enabled` | `boolean` | True plays the ready queue; false waits for play after each clip. _(default `false`)_ |
570
+
571
+ #### JavaScript
572
+
573
+ ```typescript
574
+ import { H3ReferenceToVideoTurboRealtimeModel } from "@reactor-models/h3-reference-to-video-turbo-realtime";
575
+
576
+ const h3ReferenceToVideoTurboRealtime = new H3ReferenceToVideoTurboRealtimeModel();
577
+ await h3ReferenceToVideoTurboRealtime.connect(jwt);
578
+
579
+ const reply = await h3ReferenceToVideoTurboRealtime.setAutoplay({ enabled: false });
580
+
581
+ if (reply) {
582
+ console.log("autoplay_accepted", reply.enabled);
583
+ }
584
+ ```
585
+
586
+ #### React
587
+
588
+ ```tsx
589
+ "use client";
590
+ import { useH3ReferenceToVideoTurboRealtime } from "@reactor-models/h3-reference-to-video-turbo-realtime";
591
+
592
+ function Example() {
593
+ const { setAutoplay } = useH3ReferenceToVideoTurboRealtime();
594
+
595
+ return <button onClick={() => setAutoplay({ enabled: false })}>setAutoplay</button>;
596
+ }
597
+ ```
598
+
599
+ ### `setClipSeconds`
600
+
601
+ Set the duration for future enqueues, aligned to a supported frame count. Replies clip_length_accepted and emits state_update; invalid lengths emit command_error. Queued clips keep their lengths.
602
+
603
+ Returns: [`clip_length_accepted`](#clip_length_accepted) — `{ type: "clip_length_accepted", frames: 0, clip_seconds: 0 }` (or `undefined` when the send fails).
604
+
605
+ | Parameter | Type | Description |
606
+ |---|---|---|
607
+ | `seconds` | `number` | Requested duration between 5 and 15.084 seconds, rounded to frames then aligned upward to a supported length. The reply reports actual duration and frame count. _(min 5, max 15.084, default `15`)_ |
608
+
609
+ #### JavaScript
610
+
611
+ ```typescript
612
+ import { H3ReferenceToVideoTurboRealtimeModel } from "@reactor-models/h3-reference-to-video-turbo-realtime";
613
+
614
+ const h3ReferenceToVideoTurboRealtime = new H3ReferenceToVideoTurboRealtimeModel();
615
+ await h3ReferenceToVideoTurboRealtime.connect(jwt);
616
+
617
+ const reply = await h3ReferenceToVideoTurboRealtime.setClipSeconds({ seconds: 15 });
618
+
619
+ if (reply) {
620
+ console.log("clip_length_accepted", reply.frames, reply.clip_seconds);
621
+ }
622
+ ```
623
+
624
+ #### React
625
+
626
+ ```tsx
627
+ "use client";
628
+ import { useH3ReferenceToVideoTurboRealtime } from "@reactor-models/h3-reference-to-video-turbo-realtime";
629
+
630
+ function Example() {
631
+ const { setClipSeconds } = useH3ReferenceToVideoTurboRealtime();
632
+
633
+ return <button onClick={() => setClipSeconds({ seconds: 15 })}>setClipSeconds</button>;
634
+ }
635
+ ```
636
+
637
+ ### `setFlushOnClipEnd`
638
+
639
+ Choose black or a held last frame at boundaries. Off carries the playback clock across ready autoplay clips without flushing. Does not guarantee visual continuity; reset always clears tracks. Replies flush_accepted and emits state_update.
640
+
641
+ Returns: [`flush_accepted`](#flush_accepted) — `{ type: "flush_accepted", enabled: true }` (or `undefined` when the send fails).
642
+
643
+ | Parameter | Type | Description |
644
+ |---|---|---|
645
+ | `enabled` | `boolean` | True flushes to black; false holds the last frame and avoids flushing between autoplay clips. _(default `true`)_ |
646
+
647
+ #### JavaScript
648
+
649
+ ```typescript
650
+ import { H3ReferenceToVideoTurboRealtimeModel } from "@reactor-models/h3-reference-to-video-turbo-realtime";
651
+
652
+ const h3ReferenceToVideoTurboRealtime = new H3ReferenceToVideoTurboRealtimeModel();
653
+ await h3ReferenceToVideoTurboRealtime.connect(jwt);
654
+
655
+ const reply = await h3ReferenceToVideoTurboRealtime.setFlushOnClipEnd({ enabled: true });
656
+
657
+ if (reply) {
658
+ console.log("flush_accepted", reply.enabled);
659
+ }
660
+ ```
661
+
662
+ #### React
663
+
664
+ ```tsx
665
+ "use client";
666
+ import { useH3ReferenceToVideoTurboRealtime } from "@reactor-models/h3-reference-to-video-turbo-realtime";
667
+
668
+ function Example() {
669
+ const { setFlushOnClipEnd } = useH3ReferenceToVideoTurboRealtime();
670
+
671
+ return <button onClick={() => setFlushOnClipEnd({ enabled: true })}>setFlushOnClipEnd</button>;
672
+ }
673
+ ```
674
+
675
+ ## Messages
676
+
677
+ Model-to-client messages. Register a typed listener with `on…` on `H3ReferenceToVideoTurboRealtimeModel`, or a `useH3ReferenceToVideoTurboRealtime…` hook in React, to receive only the messages you care about.
678
+
679
+ ### `clip_moved`
680
+
681
+ Emitted as the correlated reply when move changes queue order.
682
+
683
+ Listener: `onClipMoved` · React hook: `useH3ReferenceToVideoTurboRealtimeClipMoved`
684
+
685
+ | Field | Type | Description |
686
+ |---|---|---|
687
+ | `clip` | `{ "seed": number; "ready": boolean; "frames": number; "prompt": string; "clip_id": string; "seconds": number; "metadata": string; "has_reference_image"?: boolean; "reference_image_count"?: number }` | Repositioned clip. |
688
+ | `queue` | `string` | Queue containing the clip: generation or playout. |
689
+ | `position` | `number` | Resulting position, 0 = front. |
690
+
691
+ #### JavaScript
692
+
693
+ ```typescript
694
+ import { H3ReferenceToVideoTurboRealtimeModel } from "@reactor-models/h3-reference-to-video-turbo-realtime";
695
+
696
+ const h3ReferenceToVideoTurboRealtime = new H3ReferenceToVideoTurboRealtimeModel();
697
+ h3ReferenceToVideoTurboRealtime.onClipMoved((msg) => {
698
+ console.log(
699
+ "clip_moved",
700
+ msg.clip,
701
+ msg.queue,
702
+ msg.position,
703
+ );
704
+ });
705
+ await h3ReferenceToVideoTurboRealtime.connect(jwt);
706
+ ```
707
+
708
+ #### React
709
+
710
+ ```tsx
711
+ import { useH3ReferenceToVideoTurboRealtimeClipMoved } from "@reactor-models/h3-reference-to-video-turbo-realtime";
712
+
713
+ // Inside a React component wrapped by <H3ReferenceToVideoTurboRealtimeProvider>:
714
+ useH3ReferenceToVideoTurboRealtimeClipMoved((msg) => {
715
+ console.log(
716
+ "clip_moved",
717
+ msg.clip,
718
+ msg.queue,
719
+ msg.position,
720
+ );
721
+ });
722
+ ```
723
+
724
+ ### `clip_failed`
725
+
726
+ Emitted when generation fails; the clip is removed and the queue moves on.
727
+
728
+ Listener: `onClipFailed` · React hook: `useH3ReferenceToVideoTurboRealtimeClipFailed`
729
+
730
+ | Field | Type | Description |
731
+ |---|---|---|
732
+ | `clip` | `{ "seed": number; "ready": boolean; "frames": number; "prompt": string; "clip_id": string; "seconds": number; "metadata": string; "has_reference_image"?: boolean; "reference_image_count"?: number }` | Failed clip. |
733
+ | `reason` | `string` | Why generation failed. |
734
+
735
+ #### JavaScript
736
+
737
+ ```typescript
738
+ import { H3ReferenceToVideoTurboRealtimeModel } from "@reactor-models/h3-reference-to-video-turbo-realtime";
739
+
740
+ const h3ReferenceToVideoTurboRealtime = new H3ReferenceToVideoTurboRealtimeModel();
741
+ h3ReferenceToVideoTurboRealtime.onClipFailed((msg) => {
742
+ console.log("clip_failed", msg.clip, msg.reason);
743
+ });
744
+ await h3ReferenceToVideoTurboRealtime.connect(jwt);
745
+ ```
746
+
747
+ #### React
748
+
749
+ ```tsx
750
+ import { useH3ReferenceToVideoTurboRealtimeClipFailed } from "@reactor-models/h3-reference-to-video-turbo-realtime";
751
+
752
+ // Inside a React component wrapped by <H3ReferenceToVideoTurboRealtimeProvider>:
753
+ useH3ReferenceToVideoTurboRealtimeClipFailed((msg) => {
754
+ console.log("clip_failed", msg.clip, msg.reason);
755
+ });
756
+ ```
757
+
758
+ ### `clip_popped`
759
+
760
+ Emitted as the correlated reply when pop removes a queued clip.
761
+
762
+ Listener: `onClipPopped` · React hook: `useH3ReferenceToVideoTurboRealtimeClipPopped`
763
+
764
+ | Field | Type | Description |
765
+ |---|---|---|
766
+ | `clip` | `{ "seed": number; "ready": boolean; "frames": number; "prompt": string; "clip_id": string; "seconds": number; "metadata": string; "has_reference_image"?: boolean; "reference_image_count"?: number }` | Removed clip; an in-flight result will be discarded. |
767
+
768
+ #### JavaScript
769
+
770
+ ```typescript
771
+ import { H3ReferenceToVideoTurboRealtimeModel } from "@reactor-models/h3-reference-to-video-turbo-realtime";
772
+
773
+ const h3ReferenceToVideoTurboRealtime = new H3ReferenceToVideoTurboRealtimeModel();
774
+ h3ReferenceToVideoTurboRealtime.onClipPopped((msg) => {
775
+ console.log("clip_popped", msg.clip);
776
+ });
777
+ await h3ReferenceToVideoTurboRealtime.connect(jwt);
778
+ ```
779
+
780
+ #### React
781
+
782
+ ```tsx
783
+ import { useH3ReferenceToVideoTurboRealtimeClipPopped } from "@reactor-models/h3-reference-to-video-turbo-realtime";
784
+
785
+ // Inside a React component wrapped by <H3ReferenceToVideoTurboRealtimeProvider>:
786
+ useH3ReferenceToVideoTurboRealtimeClipPopped((msg) => {
787
+ console.log("clip_popped", msg.clip);
788
+ });
789
+ ```
790
+
791
+ ### `clip_queued`
792
+
793
+ Emitted as the correlated reply when enqueue accepts a clip.
794
+
795
+ Listener: `onClipQueued` · React hook: `useH3ReferenceToVideoTurboRealtimeClipQueued`
796
+
797
+ | Field | Type | Description |
798
+ |---|---|---|
799
+ | `clip` | `{ "seed": number; "ready": boolean; "frames": number; "prompt": string; "clip_id": string; "seconds": number; "metadata": string; "has_reference_image"?: boolean; "reference_image_count"?: number }` | Accepted clip, including its UUID; ready is false. |
800
+
801
+ #### JavaScript
802
+
803
+ ```typescript
804
+ import { H3ReferenceToVideoTurboRealtimeModel } from "@reactor-models/h3-reference-to-video-turbo-realtime";
805
+
806
+ const h3ReferenceToVideoTurboRealtime = new H3ReferenceToVideoTurboRealtimeModel();
807
+ h3ReferenceToVideoTurboRealtime.onClipQueued((msg) => {
808
+ console.log("clip_queued", msg.clip);
809
+ });
810
+ await h3ReferenceToVideoTurboRealtime.connect(jwt);
811
+ ```
812
+
813
+ #### React
814
+
815
+ ```tsx
816
+ import { useH3ReferenceToVideoTurboRealtimeClipQueued } from "@reactor-models/h3-reference-to-video-turbo-realtime";
817
+
818
+ // Inside a React component wrapped by <H3ReferenceToVideoTurboRealtimeProvider>:
819
+ useH3ReferenceToVideoTurboRealtimeClipQueued((msg) => {
820
+ console.log("clip_queued", msg.clip);
821
+ });
822
+ ```
823
+
824
+ ### `clip_started`
825
+
826
+ Emitted when a clip starts sending to the output tracks.
827
+
828
+ Listener: `onClipStarted` · React hook: `useH3ReferenceToVideoTurboRealtimeClipStarted`
829
+
830
+ | Field | Type | Description |
831
+ |---|---|---|
832
+ | `clip` | `{ "seed": number; "ready": boolean; "frames": number; "prompt": string; "clip_id": string; "seconds": number; "metadata": string; "has_reference_image"?: boolean; "reference_image_count"?: number }` | Clip now playing. |
833
+
834
+ #### JavaScript
835
+
836
+ ```typescript
837
+ import { H3ReferenceToVideoTurboRealtimeModel } from "@reactor-models/h3-reference-to-video-turbo-realtime";
838
+
839
+ const h3ReferenceToVideoTurboRealtime = new H3ReferenceToVideoTurboRealtimeModel();
840
+ h3ReferenceToVideoTurboRealtime.onClipStarted((msg) => {
841
+ console.log("clip_started", msg.clip);
842
+ });
843
+ await h3ReferenceToVideoTurboRealtime.connect(jwt);
844
+ ```
845
+
846
+ #### React
847
+
848
+ ```tsx
849
+ import { useH3ReferenceToVideoTurboRealtimeClipStarted } from "@reactor-models/h3-reference-to-video-turbo-realtime";
850
+
851
+ // Inside a React component wrapped by <H3ReferenceToVideoTurboRealtimeProvider>:
852
+ useH3ReferenceToVideoTurboRealtimeClipStarted((msg) => {
853
+ console.log("clip_started", msg.clip);
854
+ });
855
+ ```
856
+
857
+ ### `clip_stopped`
858
+
859
+ Emitted when stop or reset cuts a clip; it cannot be resumed.
860
+
861
+ Listener: `onClipStopped` · React hook: `useH3ReferenceToVideoTurboRealtimeClipStopped`
862
+
863
+ | Field | Type | Description |
864
+ |---|---|---|
865
+ | `clip` | `{ "seed": number; "ready": boolean; "frames": number; "prompt": string; "clip_id": string; "seconds": number; "metadata": string; "has_reference_image"?: boolean; "reference_image_count"?: number }` | Interrupted clip. |
866
+ | `seconds_sent` | `number` | Total seconds sent in the session. |
867
+
868
+ #### JavaScript
869
+
870
+ ```typescript
871
+ import { H3ReferenceToVideoTurboRealtimeModel } from "@reactor-models/h3-reference-to-video-turbo-realtime";
872
+
873
+ const h3ReferenceToVideoTurboRealtime = new H3ReferenceToVideoTurboRealtimeModel();
874
+ h3ReferenceToVideoTurboRealtime.onClipStopped((msg) => {
875
+ console.log("clip_stopped", msg.clip, msg.seconds_sent);
876
+ });
877
+ await h3ReferenceToVideoTurboRealtime.connect(jwt);
878
+ ```
879
+
880
+ #### React
881
+
882
+ ```tsx
883
+ import { useH3ReferenceToVideoTurboRealtimeClipStopped } from "@reactor-models/h3-reference-to-video-turbo-realtime";
884
+
885
+ // Inside a React component wrapped by <H3ReferenceToVideoTurboRealtimeProvider>:
886
+ useH3ReferenceToVideoTurboRealtimeClipStopped((msg) => {
887
+ console.log("clip_stopped", msg.clip, msg.seconds_sent);
888
+ });
889
+ ```
890
+
891
+ ### `queue_update`
892
+
893
+ Emitted on connect and queue changes; also answers get_queue.
894
+
895
+ Listener: `onQueueUpdate` · React hook: `useH3ReferenceToVideoTurboRealtimeQueueUpdate`
896
+
897
+ | Field | Type | Description |
898
+ |---|---|---|
899
+ | `history` | `{ "seed": number; "ready": boolean; "frames": number; "prompt": string; "clip_id": string; "seconds": number; "metadata": string; "has_reference_image"?: boolean; "reference_image_count"?: number }[]` | Always empty: a played clip is not listed here and cannot be replayed. To continue from a generated clip, pass its clip_id as `enqueue.continue_from_clip_id`. |
900
+ | `playout` | `{ "seed": number; "ready": boolean; "frames": number; "prompt": string; "clip_id": string; "seconds": number; "metadata": string; "has_reference_image"?: boolean; "reference_image_count"?: number }[]` | Built clips ready to play, front first. |
901
+ | `generation` | `{ "seed": number; "ready": boolean; "frames": number; "prompt": string; "clip_id": string; "seconds": number; "metadata": string; "has_reference_image"?: boolean; "reference_image_count"?: number }[]` | Waiting/in-flight builds, front first. |
902
+
903
+ #### JavaScript
904
+
905
+ ```typescript
906
+ import { H3ReferenceToVideoTurboRealtimeModel } from "@reactor-models/h3-reference-to-video-turbo-realtime";
907
+
908
+ const h3ReferenceToVideoTurboRealtime = new H3ReferenceToVideoTurboRealtimeModel();
909
+ h3ReferenceToVideoTurboRealtime.onQueueUpdate((msg) => {
910
+ console.log(
911
+ "queue_update",
912
+ msg.history,
913
+ msg.playout,
914
+ msg.generation,
915
+ );
916
+ });
917
+ await h3ReferenceToVideoTurboRealtime.connect(jwt);
918
+ ```
919
+
920
+ #### React
921
+
922
+ ```tsx
923
+ import { useH3ReferenceToVideoTurboRealtimeQueueUpdate } from "@reactor-models/h3-reference-to-video-turbo-realtime";
924
+
925
+ // Inside a React component wrapped by <H3ReferenceToVideoTurboRealtimeProvider>:
926
+ useH3ReferenceToVideoTurboRealtimeQueueUpdate((msg) => {
927
+ console.log(
928
+ "queue_update",
929
+ msg.history,
930
+ msg.playout,
931
+ msg.generation,
932
+ );
933
+ });
934
+ ```
935
+
936
+ ### `state_update`
937
+
938
+ Emitted on connect and after session state changes; also answers get_state.
939
+
940
+ Listener: `onStateUpdate` · React hook: `useH3ReferenceToVideoTurboRealtimeStateUpdate`
941
+
942
+ | Field | Type | Description |
943
+ |---|---|---|
944
+ | `seed` | `number` | Next automatically assigned seed. |
945
+ | `width` | `number` | Output video width in pixels. |
946
+ | `aspect` | `string` | Session aspect ratio. |
947
+ | `height` | `number` | Output video height in pixels. |
948
+ | `playing` | `boolean` | A clip is armed or playing on the output tracks. |
949
+ | `autoplay` | `boolean` | Ready clips start automatically when idle. |
950
+ | `clip_seconds` | `number` | Actual duration used by enqueues without their own seconds. |
951
+ | `clips_played` | `number` | Clips finished or stopped during this session. |
952
+ | `seconds_sent` | `number` | Seconds sent on the tracks during this session. |
953
+ | `playout_queued` | `number` | Ready clips waiting to play. |
954
+ | `valid_commands` | `string[]` | Commands permitted by current queue/playback state; their arguments still need validation. |
955
+ | `playing_clip_id` | `string \| null` | Playing clip UUID, or null when idle. |
956
+ | `clip_seconds_max` | `number` | Maximum accepted requested duration. |
957
+ | `clip_seconds_min` | `number` | Minimum accepted requested duration. |
958
+ | `playout_capacity` | `number` | Maximum ready queue size; building pauses while full. |
959
+ | `flush_on_clip_end` | `boolean` | True flushes to black at boundaries; false holds the last frame and avoids flushing between ready autoplay clips. |
960
+ | `generation_queued` | `number` | Clips in the generation queue, including the running build. |
961
+ | `generation_capacity` | `number` | Maximum generation queue size. |
962
+
963
+ #### JavaScript
964
+
965
+ ```typescript
966
+ import { H3ReferenceToVideoTurboRealtimeModel } from "@reactor-models/h3-reference-to-video-turbo-realtime";
967
+
968
+ const h3ReferenceToVideoTurboRealtime = new H3ReferenceToVideoTurboRealtimeModel();
969
+ h3ReferenceToVideoTurboRealtime.onStateUpdate((msg) => {
970
+ console.log(
971
+ "state_update",
972
+ msg.seed,
973
+ msg.width,
974
+ msg.aspect,
975
+ msg.height,
976
+ msg.playing,
977
+ msg.autoplay,
978
+ msg.clip_seconds,
979
+ msg.clips_played,
980
+ msg.seconds_sent,
981
+ msg.playout_queued,
982
+ msg.valid_commands,
983
+ msg.playing_clip_id,
984
+ msg.clip_seconds_max,
985
+ msg.clip_seconds_min,
986
+ msg.playout_capacity,
987
+ msg.flush_on_clip_end,
988
+ msg.generation_queued,
989
+ msg.generation_capacity,
990
+ );
991
+ });
992
+ await h3ReferenceToVideoTurboRealtime.connect(jwt);
993
+ ```
994
+
995
+ #### React
996
+
997
+ ```tsx
998
+ import { useH3ReferenceToVideoTurboRealtimeStateUpdate } from "@reactor-models/h3-reference-to-video-turbo-realtime";
999
+
1000
+ // Inside a React component wrapped by <H3ReferenceToVideoTurboRealtimeProvider>:
1001
+ useH3ReferenceToVideoTurboRealtimeStateUpdate((msg) => {
1002
+ console.log(
1003
+ "state_update",
1004
+ msg.seed,
1005
+ msg.width,
1006
+ msg.aspect,
1007
+ msg.height,
1008
+ msg.playing,
1009
+ msg.autoplay,
1010
+ msg.clip_seconds,
1011
+ msg.clips_played,
1012
+ msg.seconds_sent,
1013
+ msg.playout_queued,
1014
+ msg.valid_commands,
1015
+ msg.playing_clip_id,
1016
+ msg.clip_seconds_max,
1017
+ msg.clip_seconds_min,
1018
+ msg.playout_capacity,
1019
+ msg.flush_on_clip_end,
1020
+ msg.generation_queued,
1021
+ msg.generation_capacity,
1022
+ );
1023
+ });
1024
+ ```
1025
+
1026
+ ### `clip_finished`
1027
+
1028
+ Emitted when all frames and synchronized audio have been sent.
1029
+
1030
+ Listener: `onClipFinished` · React hook: `useH3ReferenceToVideoTurboRealtimeClipFinished`
1031
+
1032
+ | Field | Type | Description |
1033
+ |---|---|---|
1034
+ | `clip` | `{ "seed": number; "ready": boolean; "frames": number; "prompt": string; "clip_id": string; "seconds": number; "metadata": string; "has_reference_image"?: boolean; "reference_image_count"?: number }` | Completed clip. |
1035
+ | `seconds_sent` | `number` | Total seconds sent in the session, including this clip. |
1036
+
1037
+ #### JavaScript
1038
+
1039
+ ```typescript
1040
+ import { H3ReferenceToVideoTurboRealtimeModel } from "@reactor-models/h3-reference-to-video-turbo-realtime";
1041
+
1042
+ const h3ReferenceToVideoTurboRealtime = new H3ReferenceToVideoTurboRealtimeModel();
1043
+ h3ReferenceToVideoTurboRealtime.onClipFinished((msg) => {
1044
+ console.log("clip_finished", msg.clip, msg.seconds_sent);
1045
+ });
1046
+ await h3ReferenceToVideoTurboRealtime.connect(jwt);
1047
+ ```
1048
+
1049
+ #### React
1050
+
1051
+ ```tsx
1052
+ import { useH3ReferenceToVideoTurboRealtimeClipFinished } from "@reactor-models/h3-reference-to-video-turbo-realtime";
1053
+
1054
+ // Inside a React component wrapped by <H3ReferenceToVideoTurboRealtimeProvider>:
1055
+ useH3ReferenceToVideoTurboRealtimeClipFinished((msg) => {
1056
+ console.log("clip_finished", msg.clip, msg.seconds_sent);
1057
+ });
1058
+ ```
1059
+
1060
+ ### `command_error`
1061
+
1062
+ Emitted when a command is refused without changing the queue/defaults.
1063
+
1064
+ Listener: `onCommandError` · React hook: `useH3ReferenceToVideoTurboRealtimeCommandError`
1065
+
1066
+ | Field | Type | Description |
1067
+ |---|---|---|
1068
+ | `reason` | `string` | Why the command was refused. |
1069
+ | `command` | `string` | Refused command name. |
1070
+
1071
+ #### JavaScript
1072
+
1073
+ ```typescript
1074
+ import { H3ReferenceToVideoTurboRealtimeModel } from "@reactor-models/h3-reference-to-video-turbo-realtime";
1075
+
1076
+ const h3ReferenceToVideoTurboRealtime = new H3ReferenceToVideoTurboRealtimeModel();
1077
+ h3ReferenceToVideoTurboRealtime.onCommandError((msg) => {
1078
+ console.log("command_error", msg.reason, msg.command);
1079
+ });
1080
+ await h3ReferenceToVideoTurboRealtime.connect(jwt);
1081
+ ```
1082
+
1083
+ #### React
1084
+
1085
+ ```tsx
1086
+ import { useH3ReferenceToVideoTurboRealtimeCommandError } from "@reactor-models/h3-reference-to-video-turbo-realtime";
1087
+
1088
+ // Inside a React component wrapped by <H3ReferenceToVideoTurboRealtimeProvider>:
1089
+ useH3ReferenceToVideoTurboRealtimeCommandError((msg) => {
1090
+ console.log("command_error", msg.reason, msg.command);
1091
+ });
1092
+ ```
1093
+
1094
+ ### `seed_accepted`
1095
+
1096
+ Emitted as the correlated reply when set_seed is accepted.
1097
+
1098
+ Listener: `onSeedAccepted` · React hook: `useH3ReferenceToVideoTurboRealtimeSeedAccepted`
1099
+
1100
+ | Field | Type | Description |
1101
+ |---|---|---|
1102
+ | `seed` | `number` | Next automatically assigned seed. |
1103
+
1104
+ #### JavaScript
1105
+
1106
+ ```typescript
1107
+ import { H3ReferenceToVideoTurboRealtimeModel } from "@reactor-models/h3-reference-to-video-turbo-realtime";
1108
+
1109
+ const h3ReferenceToVideoTurboRealtime = new H3ReferenceToVideoTurboRealtimeModel();
1110
+ h3ReferenceToVideoTurboRealtime.onSeedAccepted((msg) => {
1111
+ console.log("seed_accepted", msg.seed);
1112
+ });
1113
+ await h3ReferenceToVideoTurboRealtime.connect(jwt);
1114
+ ```
1115
+
1116
+ #### React
1117
+
1118
+ ```tsx
1119
+ import { useH3ReferenceToVideoTurboRealtimeSeedAccepted } from "@reactor-models/h3-reference-to-video-turbo-realtime";
1120
+
1121
+ // Inside a React component wrapped by <H3ReferenceToVideoTurboRealtimeProvider>:
1122
+ useH3ReferenceToVideoTurboRealtimeSeedAccepted((msg) => {
1123
+ console.log("seed_accepted", msg.seed);
1124
+ });
1125
+ ```
1126
+
1127
+ ### `session_reset`
1128
+
1129
+ Emitted as the correlated reply when reset clears the playlist and defaults.
1130
+
1131
+ Listener: `onSessionReset` · React hook: `useH3ReferenceToVideoTurboRealtimeSessionReset`
1132
+
1133
+ | Field | Type | Description |
1134
+ |---|---|---|
1135
+ | `was_playing` | `boolean` | Whether reset cut an armed or playing clip. |
1136
+ | `cleared_clips` | `number` | Clips removed from both queues, excluding the playing clip. |
1137
+
1138
+ #### JavaScript
1139
+
1140
+ ```typescript
1141
+ import { H3ReferenceToVideoTurboRealtimeModel } from "@reactor-models/h3-reference-to-video-turbo-realtime";
1142
+
1143
+ const h3ReferenceToVideoTurboRealtime = new H3ReferenceToVideoTurboRealtimeModel();
1144
+ h3ReferenceToVideoTurboRealtime.onSessionReset((msg) => {
1145
+ console.log("session_reset", msg.was_playing, msg.cleared_clips);
1146
+ });
1147
+ await h3ReferenceToVideoTurboRealtime.connect(jwt);
1148
+ ```
1149
+
1150
+ #### React
1151
+
1152
+ ```tsx
1153
+ import { useH3ReferenceToVideoTurboRealtimeSessionReset } from "@reactor-models/h3-reference-to-video-turbo-realtime";
1154
+
1155
+ // Inside a React component wrapped by <H3ReferenceToVideoTurboRealtimeProvider>:
1156
+ useH3ReferenceToVideoTurboRealtimeSessionReset((msg) => {
1157
+ console.log("session_reset", msg.was_playing, msg.cleared_clips);
1158
+ });
1159
+ ```
1160
+
1161
+ ### `clip_generated`
1162
+
1163
+ Emitted when a build enters the playout queue.
1164
+
1165
+ Listener: `onClipGenerated` · React hook: `useH3ReferenceToVideoTurboRealtimeClipGenerated`
1166
+
1167
+ | Field | Type | Description |
1168
+ |---|---|---|
1169
+ | `clip` | `{ "seed": number; "ready": boolean; "frames": number; "prompt": string; "clip_id": string; "seconds": number; "metadata": string; "has_reference_image"?: boolean; "reference_image_count"?: number }` | Built clip, ready to play. |
1170
+
1171
+ #### JavaScript
1172
+
1173
+ ```typescript
1174
+ import { H3ReferenceToVideoTurboRealtimeModel } from "@reactor-models/h3-reference-to-video-turbo-realtime";
1175
+
1176
+ const h3ReferenceToVideoTurboRealtime = new H3ReferenceToVideoTurboRealtimeModel();
1177
+ h3ReferenceToVideoTurboRealtime.onClipGenerated((msg) => {
1178
+ console.log("clip_generated", msg.clip);
1179
+ });
1180
+ await h3ReferenceToVideoTurboRealtime.connect(jwt);
1181
+ ```
1182
+
1183
+ #### React
1184
+
1185
+ ```tsx
1186
+ import { useH3ReferenceToVideoTurboRealtimeClipGenerated } from "@reactor-models/h3-reference-to-video-turbo-realtime";
1187
+
1188
+ // Inside a React component wrapped by <H3ReferenceToVideoTurboRealtimeProvider>:
1189
+ useH3ReferenceToVideoTurboRealtimeClipGenerated((msg) => {
1190
+ console.log("clip_generated", msg.clip);
1191
+ });
1192
+ ```
1193
+
1194
+ ### `flush_accepted`
1195
+
1196
+ Emitted as the correlated reply when set_flush_on_clip_end is accepted.
1197
+
1198
+ Listener: `onFlushAccepted` · React hook: `useH3ReferenceToVideoTurboRealtimeFlushAccepted`
1199
+
1200
+ | Field | Type | Description |
1201
+ |---|---|---|
1202
+ | `enabled` | `boolean` | Whether clip boundaries flush the output to black. |
1203
+
1204
+ #### JavaScript
1205
+
1206
+ ```typescript
1207
+ import { H3ReferenceToVideoTurboRealtimeModel } from "@reactor-models/h3-reference-to-video-turbo-realtime";
1208
+
1209
+ const h3ReferenceToVideoTurboRealtime = new H3ReferenceToVideoTurboRealtimeModel();
1210
+ h3ReferenceToVideoTurboRealtime.onFlushAccepted((msg) => {
1211
+ console.log("flush_accepted", msg.enabled);
1212
+ });
1213
+ await h3ReferenceToVideoTurboRealtime.connect(jwt);
1214
+ ```
1215
+
1216
+ #### React
1217
+
1218
+ ```tsx
1219
+ import { useH3ReferenceToVideoTurboRealtimeFlushAccepted } from "@reactor-models/h3-reference-to-video-turbo-realtime";
1220
+
1221
+ // Inside a React component wrapped by <H3ReferenceToVideoTurboRealtimeProvider>:
1222
+ useH3ReferenceToVideoTurboRealtimeFlushAccepted((msg) => {
1223
+ console.log("flush_accepted", msg.enabled);
1224
+ });
1225
+ ```
1226
+
1227
+ ### `canvas_accepted`
1228
+
1229
+ Emitted as the correlated reply when set_canvas is accepted.
1230
+
1231
+ Listener: `onCanvasAccepted` · React hook: `useH3ReferenceToVideoTurboRealtimeCanvasAccepted`
1232
+
1233
+ | Field | Type | Description |
1234
+ |---|---|---|
1235
+ | `width` | `number` | Video width in pixels. |
1236
+ | `aspect` | `string` | Accepted aspect ratio. |
1237
+ | `height` | `number` | Video height in pixels. |
1238
+
1239
+ #### JavaScript
1240
+
1241
+ ```typescript
1242
+ import { H3ReferenceToVideoTurboRealtimeModel } from "@reactor-models/h3-reference-to-video-turbo-realtime";
1243
+
1244
+ const h3ReferenceToVideoTurboRealtime = new H3ReferenceToVideoTurboRealtimeModel();
1245
+ h3ReferenceToVideoTurboRealtime.onCanvasAccepted((msg) => {
1246
+ console.log(
1247
+ "canvas_accepted",
1248
+ msg.width,
1249
+ msg.aspect,
1250
+ msg.height,
1251
+ );
1252
+ });
1253
+ await h3ReferenceToVideoTurboRealtime.connect(jwt);
1254
+ ```
1255
+
1256
+ #### React
1257
+
1258
+ ```tsx
1259
+ import { useH3ReferenceToVideoTurboRealtimeCanvasAccepted } from "@reactor-models/h3-reference-to-video-turbo-realtime";
1260
+
1261
+ // Inside a React component wrapped by <H3ReferenceToVideoTurboRealtimeProvider>:
1262
+ useH3ReferenceToVideoTurboRealtimeCanvasAccepted((msg) => {
1263
+ console.log(
1264
+ "canvas_accepted",
1265
+ msg.width,
1266
+ msg.aspect,
1267
+ msg.height,
1268
+ );
1269
+ });
1270
+ ```
1271
+
1272
+ ### `autoplay_accepted`
1273
+
1274
+ Emitted as the correlated reply when set_autoplay is accepted.
1275
+
1276
+ Listener: `onAutoplayAccepted` · React hook: `useH3ReferenceToVideoTurboRealtimeAutoplayAccepted`
1277
+
1278
+ | Field | Type | Description |
1279
+ |---|---|---|
1280
+ | `enabled` | `boolean` | Whether ready clips start automatically. |
1281
+
1282
+ #### JavaScript
1283
+
1284
+ ```typescript
1285
+ import { H3ReferenceToVideoTurboRealtimeModel } from "@reactor-models/h3-reference-to-video-turbo-realtime";
1286
+
1287
+ const h3ReferenceToVideoTurboRealtime = new H3ReferenceToVideoTurboRealtimeModel();
1288
+ h3ReferenceToVideoTurboRealtime.onAutoplayAccepted((msg) => {
1289
+ console.log("autoplay_accepted", msg.enabled);
1290
+ });
1291
+ await h3ReferenceToVideoTurboRealtime.connect(jwt);
1292
+ ```
1293
+
1294
+ #### React
1295
+
1296
+ ```tsx
1297
+ import { useH3ReferenceToVideoTurboRealtimeAutoplayAccepted } from "@reactor-models/h3-reference-to-video-turbo-realtime";
1298
+
1299
+ // Inside a React component wrapped by <H3ReferenceToVideoTurboRealtimeProvider>:
1300
+ useH3ReferenceToVideoTurboRealtimeAutoplayAccepted((msg) => {
1301
+ console.log("autoplay_accepted", msg.enabled);
1302
+ });
1303
+ ```
1304
+
1305
+ ### `clip_length_accepted`
1306
+
1307
+ Emitted as the correlated reply when set_clip_seconds is accepted.
1308
+
1309
+ Listener: `onClipLengthAccepted` · React hook: `useH3ReferenceToVideoTurboRealtimeClipLengthAccepted`
1310
+
1311
+ | Field | Type | Description |
1312
+ |---|---|---|
1313
+ | `frames` | `number` | Effective default frame count. |
1314
+ | `clip_seconds` | `number` | Effective default duration, in seconds. |
1315
+
1316
+ #### JavaScript
1317
+
1318
+ ```typescript
1319
+ import { H3ReferenceToVideoTurboRealtimeModel } from "@reactor-models/h3-reference-to-video-turbo-realtime";
1320
+
1321
+ const h3ReferenceToVideoTurboRealtime = new H3ReferenceToVideoTurboRealtimeModel();
1322
+ h3ReferenceToVideoTurboRealtime.onClipLengthAccepted((msg) => {
1323
+ console.log("clip_length_accepted", msg.frames, msg.clip_seconds);
1324
+ });
1325
+ await h3ReferenceToVideoTurboRealtime.connect(jwt);
1326
+ ```
1327
+
1328
+ #### React
1329
+
1330
+ ```tsx
1331
+ import { useH3ReferenceToVideoTurboRealtimeClipLengthAccepted } from "@reactor-models/h3-reference-to-video-turbo-realtime";
1332
+
1333
+ // Inside a React component wrapped by <H3ReferenceToVideoTurboRealtimeProvider>:
1334
+ useH3ReferenceToVideoTurboRealtimeClipLengthAccepted((msg) => {
1335
+ console.log("clip_length_accepted", msg.frames, msg.clip_seconds);
1336
+ });
1337
+ ```
1338
+
1339
+ ## Tracks
1340
+
1341
+ Named media channels between your app and the H3ReferenceToVideoTurboRealtime model. Use the typed helpers below — `H3ReferenceToVideoTurboRealtimeModel.publish<Track>` / `on<Track>` in plain JS, and `useH3ReferenceToVideoTurboRealtimeTrack` or the per-track `<H3ReferenceToVideoTurboRealtime<Track>View>` components in React — so track names are checked at compile time.
1342
+
1343
+ ### `main_video`
1344
+
1345
+ A video channel you subscribe to — the model publishes this for your app to render.
1346
+
1347
+ #### JavaScript
1348
+
1349
+ ```typescript
1350
+ import { H3ReferenceToVideoTurboRealtimeModel } from "@reactor-models/h3-reference-to-video-turbo-realtime";
1351
+
1352
+ const h3ReferenceToVideoTurboRealtime = new H3ReferenceToVideoTurboRealtimeModel();
1353
+ h3ReferenceToVideoTurboRealtime.onMainVideo((track, stream) => {
1354
+ // attach to a <video> element, pipe to a canvas, etc.
1355
+ videoEl.srcObject = stream;
1356
+ });
1357
+ await h3ReferenceToVideoTurboRealtime.connect(jwt);
1358
+ ```
1359
+
1360
+ #### React
1361
+
1362
+ ```tsx
1363
+ "use client";
1364
+ import { H3ReferenceToVideoTurboRealtimeMainVideoView } from "@reactor-models/h3-reference-to-video-turbo-realtime";
1365
+
1366
+ // Inside a component wrapped by <H3ReferenceToVideoTurboRealtimeProvider>:
1367
+ export function Example() {
1368
+ return <H3ReferenceToVideoTurboRealtimeMainVideoView className="w-full aspect-video" />;
1369
+ }
1370
+ ```
1371
+
1372
+ ### `main_audio`
1373
+
1374
+ A audio channel you subscribe to — the model publishes this for your app to render.
1375
+
1376
+ #### JavaScript
1377
+
1378
+ ```typescript
1379
+ import { H3ReferenceToVideoTurboRealtimeModel } from "@reactor-models/h3-reference-to-video-turbo-realtime";
1380
+
1381
+ const h3ReferenceToVideoTurboRealtime = new H3ReferenceToVideoTurboRealtimeModel();
1382
+ h3ReferenceToVideoTurboRealtime.onMainAudio((track, stream) => {
1383
+ // attach to a <audio> element, pipe to a canvas, etc.
1384
+ videoEl.srcObject = stream;
1385
+ });
1386
+ await h3ReferenceToVideoTurboRealtime.connect(jwt);
1387
+ ```
1388
+
1389
+ #### React
1390
+
1391
+ ```tsx
1392
+ "use client";
1393
+ import { useH3ReferenceToVideoTurboRealtimeTrack } from "@reactor-models/h3-reference-to-video-turbo-realtime";
1394
+
1395
+ // Inside a component wrapped by <H3ReferenceToVideoTurboRealtimeProvider>:
1396
+ export function Example() {
1397
+ const track = useH3ReferenceToVideoTurboRealtimeTrack("main_audio");
1398
+ // attach `track` to an <audio> element via a ref + srcObject.
1399
+ return null;
1400
+ }
1401
+ ```