varg.ai-sdk 0.1.1 → 0.4.0-alpha.1

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.
Files changed (246) hide show
  1. package/.claude/settings.local.json +1 -1
  2. package/.env.example +3 -0
  3. package/.github/workflows/ci.yml +23 -0
  4. package/.husky/README.md +102 -0
  5. package/.husky/commit-msg +6 -0
  6. package/.husky/pre-commit +9 -0
  7. package/.husky/pre-push +6 -0
  8. package/.size-limit.json +8 -0
  9. package/.test-hooks.ts +5 -0
  10. package/CLAUDE.md +10 -3
  11. package/CONTRIBUTING.md +150 -0
  12. package/LICENSE.md +53 -0
  13. package/README.md +56 -209
  14. package/SKILLS.md +26 -10
  15. package/biome.json +7 -1
  16. package/bun.lock +1286 -0
  17. package/commitlint.config.js +22 -0
  18. package/docs/index.html +1130 -0
  19. package/docs/prompting.md +326 -0
  20. package/docs/react.md +834 -0
  21. package/docs/sdk.md +812 -0
  22. package/ffmpeg/CLAUDE.md +68 -0
  23. package/package.json +43 -10
  24. package/pipeline/cookbooks/scripts/animate-frames-parallel.ts +84 -0
  25. package/pipeline/cookbooks/scripts/combine-scenes.sh +53 -0
  26. package/pipeline/cookbooks/scripts/generate-frames-parallel.ts +99 -0
  27. package/pipeline/cookbooks/scripts/still-to-video.sh +37 -0
  28. package/pipeline/cookbooks/text-to-tiktok.md +669 -0
  29. package/pipeline/cookbooks/trendwatching.md +156 -0
  30. package/plan.md +281 -0
  31. package/scripts/.gitkeep +0 -0
  32. package/src/ai-sdk/cache.ts +142 -0
  33. package/src/ai-sdk/examples/cached-generation.ts +53 -0
  34. package/src/ai-sdk/examples/duet-scene-4.ts +53 -0
  35. package/src/ai-sdk/examples/duet-scene-5-audio.ts +32 -0
  36. package/src/ai-sdk/examples/duet-video.ts +56 -0
  37. package/src/ai-sdk/examples/editly-composition.ts +63 -0
  38. package/src/ai-sdk/examples/editly-test.ts +57 -0
  39. package/src/ai-sdk/examples/editly-video-test.ts +52 -0
  40. package/src/ai-sdk/examples/fal-lipsync.ts +43 -0
  41. package/src/ai-sdk/examples/higgsfield-image.ts +61 -0
  42. package/src/ai-sdk/examples/music-generation.ts +19 -0
  43. package/src/ai-sdk/examples/openai-sora.ts +34 -0
  44. package/src/ai-sdk/examples/replicate-bg-removal.ts +52 -0
  45. package/src/ai-sdk/examples/simpsons-scene.ts +61 -0
  46. package/src/ai-sdk/examples/talking-lion.ts +55 -0
  47. package/src/ai-sdk/examples/video-generation.ts +39 -0
  48. package/src/ai-sdk/examples/workflow-animated-girl.ts +104 -0
  49. package/src/ai-sdk/examples/workflow-before-after.ts +114 -0
  50. package/src/ai-sdk/examples/workflow-character-grid.ts +112 -0
  51. package/src/ai-sdk/examples/workflow-slideshow.ts +161 -0
  52. package/src/ai-sdk/file-cache.ts +112 -0
  53. package/src/ai-sdk/file.ts +238 -0
  54. package/src/ai-sdk/generate-element.ts +92 -0
  55. package/src/ai-sdk/generate-music.ts +46 -0
  56. package/src/ai-sdk/generate-video.ts +165 -0
  57. package/src/ai-sdk/index.ts +72 -0
  58. package/src/ai-sdk/music-model.ts +110 -0
  59. package/src/ai-sdk/providers/editly/editly.test.ts +1108 -0
  60. package/src/ai-sdk/providers/editly/ffmpeg.ts +60 -0
  61. package/src/ai-sdk/providers/editly/index.ts +817 -0
  62. package/src/ai-sdk/providers/editly/layers.ts +776 -0
  63. package/src/ai-sdk/providers/editly/plan.md +144 -0
  64. package/src/ai-sdk/providers/editly/types.ts +328 -0
  65. package/src/ai-sdk/providers/elevenlabs-provider.ts +255 -0
  66. package/src/ai-sdk/providers/fal-provider.ts +512 -0
  67. package/src/ai-sdk/providers/higgsfield.ts +379 -0
  68. package/src/ai-sdk/providers/openai.ts +251 -0
  69. package/src/ai-sdk/providers/replicate.ts +16 -0
  70. package/src/ai-sdk/video-model.ts +185 -0
  71. package/src/cli/commands/find.tsx +137 -0
  72. package/src/cli/commands/help.tsx +85 -0
  73. package/src/cli/commands/index.ts +6 -0
  74. package/src/cli/commands/list.tsx +238 -0
  75. package/src/cli/commands/render.tsx +71 -0
  76. package/src/cli/commands/run.tsx +511 -0
  77. package/src/cli/commands/which.tsx +253 -0
  78. package/src/cli/index.ts +114 -0
  79. package/src/cli/quiet.ts +44 -0
  80. package/src/cli/types.ts +32 -0
  81. package/src/cli/ui/components/Badge.tsx +29 -0
  82. package/src/cli/ui/components/DataTable.tsx +51 -0
  83. package/src/cli/ui/components/Header.tsx +23 -0
  84. package/src/cli/ui/components/HelpBlock.tsx +44 -0
  85. package/src/cli/ui/components/KeyValue.tsx +33 -0
  86. package/src/cli/ui/components/OptionRow.tsx +81 -0
  87. package/src/cli/ui/components/Separator.tsx +23 -0
  88. package/src/cli/ui/components/StatusBox.tsx +108 -0
  89. package/src/cli/ui/components/VargBox.tsx +51 -0
  90. package/src/cli/ui/components/VargProgress.tsx +36 -0
  91. package/src/cli/ui/components/VargSpinner.tsx +34 -0
  92. package/src/cli/ui/components/VargText.tsx +56 -0
  93. package/src/cli/ui/components/index.ts +19 -0
  94. package/src/cli/ui/index.ts +12 -0
  95. package/src/cli/ui/render.ts +35 -0
  96. package/src/cli/ui/theme.ts +63 -0
  97. package/src/cli/utils.ts +78 -0
  98. package/src/core/executor/executor.ts +201 -0
  99. package/src/core/executor/index.ts +13 -0
  100. package/src/core/executor/job.ts +214 -0
  101. package/src/core/executor/pipeline.ts +222 -0
  102. package/src/core/index.ts +11 -0
  103. package/src/core/registry/index.ts +9 -0
  104. package/src/core/registry/loader.ts +149 -0
  105. package/src/core/registry/registry.ts +221 -0
  106. package/src/core/registry/resolver.ts +206 -0
  107. package/src/core/schema/helpers.ts +134 -0
  108. package/src/core/schema/index.ts +8 -0
  109. package/src/core/schema/shared.ts +102 -0
  110. package/src/core/schema/types.ts +279 -0
  111. package/src/core/schema/validator.ts +92 -0
  112. package/src/definitions/actions/captions.ts +261 -0
  113. package/src/definitions/actions/edit.ts +298 -0
  114. package/src/definitions/actions/image.ts +125 -0
  115. package/src/definitions/actions/index.ts +114 -0
  116. package/src/definitions/actions/music.ts +205 -0
  117. package/src/definitions/actions/sync.ts +128 -0
  118. package/{action/transcribe/index.ts → src/definitions/actions/transcribe.ts} +58 -68
  119. package/src/definitions/actions/upload.ts +111 -0
  120. package/src/definitions/actions/video.ts +163 -0
  121. package/src/definitions/actions/voice.ts +119 -0
  122. package/src/definitions/index.ts +23 -0
  123. package/src/definitions/models/elevenlabs.ts +50 -0
  124. package/src/definitions/models/flux.ts +56 -0
  125. package/src/definitions/models/index.ts +36 -0
  126. package/src/definitions/models/kling.ts +56 -0
  127. package/src/definitions/models/llama.ts +54 -0
  128. package/src/definitions/models/nano-banana-pro.ts +102 -0
  129. package/src/definitions/models/sonauto.ts +68 -0
  130. package/src/definitions/models/soul.ts +65 -0
  131. package/src/definitions/models/wan.ts +54 -0
  132. package/src/definitions/models/whisper.ts +44 -0
  133. package/src/definitions/skills/index.ts +12 -0
  134. package/src/definitions/skills/talking-character.ts +87 -0
  135. package/src/definitions/skills/text-to-tiktok.ts +97 -0
  136. package/src/index.ts +118 -0
  137. package/src/providers/apify.ts +269 -0
  138. package/src/providers/base.ts +264 -0
  139. package/src/providers/elevenlabs.ts +217 -0
  140. package/src/providers/fal.ts +392 -0
  141. package/src/providers/ffmpeg.ts +544 -0
  142. package/src/providers/fireworks.ts +193 -0
  143. package/src/providers/groq.ts +149 -0
  144. package/src/providers/higgsfield.ts +145 -0
  145. package/src/providers/index.ts +143 -0
  146. package/src/providers/replicate.ts +147 -0
  147. package/src/providers/storage.ts +206 -0
  148. package/src/react/cli.ts +52 -0
  149. package/src/react/elements.ts +146 -0
  150. package/src/react/examples/branching.tsx +66 -0
  151. package/src/react/examples/captions-demo.tsx +37 -0
  152. package/src/react/examples/character-video.tsx +84 -0
  153. package/src/react/examples/grid.tsx +53 -0
  154. package/src/react/examples/layouts-demo.tsx +57 -0
  155. package/src/react/examples/madi.tsx +60 -0
  156. package/src/react/examples/music-test.tsx +35 -0
  157. package/src/react/examples/onlyfans-1m/workflow.tsx +88 -0
  158. package/src/react/examples/orange-portrait.tsx +41 -0
  159. package/src/react/examples/split-element-demo.tsx +60 -0
  160. package/src/react/examples/split-layout-demo.tsx +60 -0
  161. package/src/react/examples/split.tsx +41 -0
  162. package/src/react/examples/video-grid.tsx +46 -0
  163. package/src/react/index.ts +43 -0
  164. package/src/react/layouts/grid.tsx +28 -0
  165. package/src/react/layouts/index.ts +2 -0
  166. package/src/react/layouts/split.tsx +20 -0
  167. package/src/react/react.test.ts +309 -0
  168. package/src/react/render.ts +21 -0
  169. package/src/react/renderers/animate.ts +59 -0
  170. package/src/react/renderers/captions.ts +297 -0
  171. package/src/react/renderers/clip.ts +248 -0
  172. package/src/react/renderers/context.ts +17 -0
  173. package/src/react/renderers/image.ts +109 -0
  174. package/src/react/renderers/index.ts +22 -0
  175. package/src/react/renderers/music.ts +60 -0
  176. package/src/react/renderers/packshot.ts +84 -0
  177. package/src/react/renderers/progress.ts +173 -0
  178. package/src/react/renderers/render.ts +243 -0
  179. package/src/react/renderers/slider.ts +69 -0
  180. package/src/react/renderers/speech.ts +53 -0
  181. package/src/react/renderers/split.ts +91 -0
  182. package/src/react/renderers/subtitle.ts +16 -0
  183. package/src/react/renderers/swipe.ts +75 -0
  184. package/src/react/renderers/title.ts +17 -0
  185. package/src/react/renderers/utils.ts +124 -0
  186. package/src/react/renderers/video.ts +127 -0
  187. package/src/react/runtime/jsx-dev-runtime.ts +43 -0
  188. package/src/react/runtime/jsx-runtime.ts +35 -0
  189. package/src/react/types.ts +232 -0
  190. package/src/studio/index.ts +26 -0
  191. package/src/studio/scanner.ts +102 -0
  192. package/src/studio/server.ts +554 -0
  193. package/src/studio/stages.ts +251 -0
  194. package/src/studio/step-renderer.ts +279 -0
  195. package/src/studio/types.ts +60 -0
  196. package/src/studio/ui/cache.html +303 -0
  197. package/src/studio/ui/index.html +1820 -0
  198. package/src/tests/all.test.ts +509 -0
  199. package/src/tests/index.ts +33 -0
  200. package/src/tests/unit.test.ts +403 -0
  201. package/tsconfig.cli.json +8 -0
  202. package/tsconfig.json +21 -3
  203. package/TEST_RESULTS.md +0 -122
  204. package/action/captions/SKILL.md +0 -170
  205. package/action/captions/index.ts +0 -169
  206. package/action/edit/SKILL.md +0 -235
  207. package/action/edit/index.ts +0 -437
  208. package/action/image/SKILL.md +0 -140
  209. package/action/image/index.ts +0 -105
  210. package/action/sync/SKILL.md +0 -136
  211. package/action/sync/index.ts +0 -145
  212. package/action/transcribe/SKILL.md +0 -179
  213. package/action/video/SKILL.md +0 -116
  214. package/action/video/index.ts +0 -125
  215. package/action/voice/SKILL.md +0 -125
  216. package/action/voice/index.ts +0 -136
  217. package/cli/commands/find.ts +0 -58
  218. package/cli/commands/help.ts +0 -70
  219. package/cli/commands/list.ts +0 -49
  220. package/cli/commands/run.ts +0 -237
  221. package/cli/commands/which.ts +0 -66
  222. package/cli/discover.ts +0 -66
  223. package/cli/index.ts +0 -33
  224. package/cli/runner.ts +0 -65
  225. package/cli/types.ts +0 -49
  226. package/cli/ui.ts +0 -185
  227. package/index.ts +0 -75
  228. package/lib/README.md +0 -144
  229. package/lib/ai-sdk/fal.ts +0 -106
  230. package/lib/ai-sdk/replicate.ts +0 -107
  231. package/lib/elevenlabs.ts +0 -382
  232. package/lib/fal.ts +0 -467
  233. package/lib/ffmpeg.ts +0 -467
  234. package/lib/fireworks.ts +0 -235
  235. package/lib/groq.ts +0 -246
  236. package/lib/higgsfield.ts +0 -176
  237. package/lib/remotion/SKILL.md +0 -823
  238. package/lib/remotion/cli.ts +0 -115
  239. package/lib/remotion/functions.ts +0 -283
  240. package/lib/remotion/index.ts +0 -19
  241. package/lib/remotion/templates.ts +0 -73
  242. package/lib/replicate.ts +0 -304
  243. package/output.txt +0 -1
  244. package/test-import.ts +0 -7
  245. package/test-services.ts +0 -97
  246. package/utilities/s3.ts +0 -147
package/docs/react.md ADDED
@@ -0,0 +1,834 @@
1
+ # varg-react
2
+
3
+ declarative video rendering with ai generation. jsx for videos.
4
+
5
+ ## quick start
6
+
7
+ ```bash
8
+ bun install @vargai/react
9
+ ```
10
+
11
+ ```tsx
12
+ import { render, Render, Clip, Image, Title } from "@vargai/react";
13
+
14
+ await render(
15
+ <Render width={1280} height={720}>
16
+ <Clip duration={5}>
17
+ <Image prompt="sunset over ocean, cinematic" />
18
+ <Title position="bottom">beautiful sunset</Title>
19
+ </Clip>
20
+ </Render>,
21
+ { output: "output/sunset.mp4" }
22
+ );
23
+ ```
24
+
25
+ ## core concepts
26
+
27
+ ### everything is cached
28
+
29
+ every element computes a cache key from its props. same props = cache hit.
30
+
31
+ ```tsx
32
+ // first run: generates image (~3s)
33
+ // second run: instant cache hit
34
+ <Image prompt="cyberpunk cityscape at night" />
35
+ ```
36
+
37
+ change any prop and it regenerates:
38
+
39
+ ```tsx
40
+ <Image prompt="cyberpunk cityscape at night" aspectRatio="9:16" />
41
+ // different aspectRatio = new cache key = regenerates
42
+ ```
43
+
44
+ ### clips and layers
45
+
46
+ clips are sequential. layers within a clip are stacked.
47
+
48
+ ```tsx
49
+ <Render width={1080} height={1920} fps={30}>
50
+ {/* first clip */}
51
+ <Clip duration={5}>
52
+ <Image prompt="coffee shop interior" />
53
+ </Clip>
54
+
55
+ {/* second clip with transition */}
56
+ <Clip duration={3} transition={{ name: "fade", duration: 0.5 }}>
57
+ <Image prompt="park with autumn leaves" />
58
+ </Clip>
59
+ </Render>
60
+ ```
61
+
62
+ ### transitions
63
+
64
+ ```tsx
65
+ <Clip transition={{ name: "fade", duration: 0.3 }}>
66
+ <Clip transition={{ name: "wipeleft", duration: 0.5 }}>
67
+ <Clip transition={{ name: "slideright", duration: 0.4 }}>
68
+ <Clip transition={{ name: "crossfade", duration: 0.5 }}>
69
+ <Clip transition={{ name: "cube", duration: 0.8 }}>
70
+ <Clip transition={{ name: "directionalwipe", duration: 0.5 }}>
71
+ ```
72
+
73
+ 67 gl-transitions available: `fade`, `crossfade`, `wipeleft`, `wiperight`, `wipeup`, `wipedown`, `slideleft`, `slideright`, `slideup`, `slidedown`, `cube`, `directionalwipe`, `dreamy`, `squareswire`, `radial`, `pixelize`, and more.
74
+
75
+ ## rendering images
76
+
77
+ ```tsx
78
+ import { Image } from "@vargai/react";
79
+
80
+ // basic
81
+ <Image prompt="ralph wiggum eating glue, simpsons style" />
82
+
83
+ // with aspect ratio
84
+ <Image prompt="fat tiger lying on couch" aspectRatio="1:1" />
85
+ <Image prompt="luigi in wheelchair racing down hill" aspectRatio="16:9" />
86
+ <Image prompt="south park cartman screaming" aspectRatio="9:16" />
87
+
88
+ // zoom animation (ken burns)
89
+ <Image prompt="epic mountain landscape" zoom="in" />
90
+ <Image prompt="white-teeth black athletic guy flexing" zoom="out" />
91
+ <Image prompt="forest path mysterious" zoom="left" />
92
+ <Image prompt="ocean waves crashing" zoom="right" />
93
+
94
+ // contain with blur (no black bars)
95
+ <Image prompt="fat tiger square photo" resize="contain-blur" />
96
+
97
+ // remove background
98
+ <Image prompt="ralph wiggum transparent" removeBackground />
99
+
100
+ // image-to-image edit
101
+ <Image
102
+ src="./photo.png"
103
+ prompt="make it look like south park style"
104
+ />
105
+ ```
106
+
107
+ ## rendering video
108
+
109
+ ### text-to-video generation
110
+
111
+ ```tsx
112
+ import { Video } from "@vargai/react";
113
+
114
+ // generate video from prompt
115
+ <Video prompt="ocean waves crashing on beach, cinematic" model={fal.videoModel("wan-2.5")} />
116
+
117
+ // use existing video file
118
+ <Video src="./interview.mp4" />
119
+
120
+ // with audio and trimming
121
+ <Video src="./clip.mp4" keepAudio volume={0.8} cutFrom={5} cutTo={15} />
122
+ ```
123
+
124
+ ### image-to-video animation
125
+
126
+ ```tsx
127
+ import { Animate } from "@vargai/react";
128
+
129
+ // animate an image
130
+ <Animate
131
+ image={<Image prompt="fat tiger on couch" />}
132
+ motion="fat tiger breathing heavily, belly jiggles"
133
+ duration={5}
134
+ />
135
+
136
+ // from existing file
137
+ <Animate
138
+ src="./luigi.png"
139
+ motion="wheelchair spinning in circles"
140
+ duration={5}
141
+ />
142
+ ```
143
+
144
+ ## rendering speech
145
+
146
+ ```tsx
147
+ import { Speech } from "@vargai/react";
148
+
149
+ <Clip>
150
+ <Image prompt="ralph wiggum at podium, simpsons style" />
151
+ <Speech voice="adam">
152
+ I'm in danger. Also I'm the CEO now.
153
+ </Speech>
154
+ </Clip>
155
+ ```
156
+
157
+ voices: `rachel`, `adam`, `bella`, `sam`, `josh`
158
+
159
+ ## talking heads
160
+
161
+ compose Image, Animate, and Speech to create talking characters:
162
+
163
+ ```tsx
164
+ import { Clip, Image, Animate, Speech } from "@vargai/react";
165
+ import { fal, elevenlabs } from "@vargai/sdk";
166
+
167
+ // define a reusable TalkingHead component
168
+ const TalkingHead = ({ character, voice, children }: {
169
+ character: string;
170
+ voice: string;
171
+ children: string;
172
+ }) => (
173
+ <>
174
+ <Animate
175
+ image={<Image prompt={character} model={fal.imageModel("flux-schnell")} />}
176
+ model={fal.videoModel("wan-2.5")}
177
+ motion="subtle talking, head movements, blinking"
178
+ />
179
+ <Speech voice={voice} model={elevenlabs.speechModel("turbo")}>
180
+ {children}
181
+ </Speech>
182
+ </>
183
+ );
184
+
185
+ // use it
186
+ <Clip duration="auto">
187
+ <TalkingHead character="south park cartman, angry face" voice="adam">
188
+ Screw you guys, I'm going home. But first let me tell you
189
+ about our sponsor, NordVPN.
190
+ </TalkingHead>
191
+ </Clip>
192
+ ```
193
+
194
+ this internally:
195
+ 1. generates character image from prompt
196
+ 2. generates speech audio from text
197
+ 3. animates image to video
198
+ 4. sets clip duration to match audio length
199
+
200
+ all steps cached independently.
201
+
202
+ ### with existing image
203
+
204
+ ```tsx
205
+ const TalkingHead = ({ src, voice, children }) => (
206
+ <>
207
+ <Animate
208
+ src={src}
209
+ model={fal.videoModel("wan-2.5")}
210
+ motion="talking naturally"
211
+ />
212
+ <Speech voice={voice} model={elevenlabs.speechModel("turbo")}>
213
+ {children}
214
+ </Speech>
215
+ </>
216
+ );
217
+
218
+ <Clip duration="auto">
219
+ <TalkingHead src="./fat-tiger.png" voice="josh">
220
+ I'm not fat, I'm cultivating mass.
221
+ </TalkingHead>
222
+ </Clip>
223
+ ```
224
+
225
+ ## overlays and pip
226
+
227
+ layer children stack on top of each other.
228
+
229
+ ```tsx
230
+ <Clip duration={5}>
231
+ {/* base layer - full frame */}
232
+ <Image prompt="luigi wheelchair racing track" />
233
+
234
+ {/* picture-in-picture overlay */}
235
+ <TalkingHead
236
+ character="white-teeth black athletic guy, sports commentator"
237
+ voice="josh"
238
+ position={{ right: "5%", bottom: "5%" }}
239
+ size={{ width: "25%", height: "25%" }}
240
+ >
241
+ And he's approaching the final turn! Incredible speed!
242
+ </TalkingHead>
243
+ </Clip>
244
+ ```
245
+
246
+ ### position presets
247
+
248
+ ```tsx
249
+ <Image position="top-left" />
250
+ <Image position="top" />
251
+ <Image position="top-right" />
252
+ <Image position="left" />
253
+ <Image position="center" />
254
+ <Image position="right" />
255
+ <Image position="bottom-left" />
256
+ <Image position="bottom" />
257
+ <Image position="bottom-right" />
258
+ ```
259
+
260
+ ## text overlays
261
+
262
+ ```tsx
263
+ import { Title, Subtitle } from "@vargai/react";
264
+
265
+ <Clip duration={5}>
266
+ <Image prompt="ralph wiggum staring blankly" />
267
+
268
+ {/* centered title */}
269
+ <Title>I'M IN DANGER</Title>
270
+
271
+ {/* positioned title */}
272
+ <Title position="top" color="#ffffff">
273
+ Episode 1
274
+ </Title>
275
+
276
+ {/* subtitle with background */}
277
+ <Subtitle>the beginning of something special</Subtitle>
278
+ </Clip>
279
+ ```
280
+
281
+ ### text timing
282
+
283
+ text appears and disappears within the clip:
284
+
285
+ ```tsx
286
+ <Clip duration={10}>
287
+ <Image prompt="fat tiger sleeping timelapse" />
288
+
289
+ {/* appears at 2s, disappears at 5s */}
290
+ <Title start={2} end={5}>
291
+ Hour 1: Still sleeping
292
+ </Title>
293
+
294
+ {/* appears at 6s, stays until clip ends */}
295
+ <Title start={6}>
296
+ Hour 47: Still sleeping
297
+ </Title>
298
+ </Clip>
299
+ ```
300
+
301
+ ## captions (tiktok-style)
302
+
303
+ word-by-word animated captions synced to speech:
304
+
305
+ ```tsx
306
+ import { Captions } from "@vargai/react";
307
+
308
+ <Clip>
309
+ <Video src="./cartman-rant.mp4" />
310
+ <Captions
311
+ src="./cartman-rant.mp4"
312
+ style="tiktok"
313
+ color="#ffffff"
314
+ activeColor="#ffff00"
315
+ fontSize={48}
316
+ />
317
+ </Clip>
318
+ ```
319
+
320
+ or feed it a speech element directly:
321
+
322
+ ```tsx
323
+ <Clip>
324
+ <Image prompt="ralph wiggum at podium" />
325
+ <Speech voice="adam" id="ralph-speech">
326
+ I'm in danger. The danger is me.
327
+ </Speech>
328
+ <Captions
329
+ src={ralph-speech}
330
+ style="tiktok"
331
+ />
332
+ </Clip>
333
+ ```
334
+
335
+ ### caption styles
336
+
337
+ ```tsx
338
+ <Captions src="./audio.mp3" style="tiktok" /> // word-by-word highlight
339
+ <Captions src="./audio.mp3" style="karaoke" /> // fill left-to-right
340
+ <Captions src="./audio.mp3" style="bounce" /> // words bounce in
341
+ <Captions src="./audio.mp3" style="typewriter" /> // typing effect
342
+ ```
343
+
344
+ ### with custom transcript
345
+
346
+ ```tsx
347
+ <Clip>
348
+ <Video src="./video.mp4" />
349
+ <Captions srt="./captions.srt" style="tiktok" />
350
+ </Clip>
351
+ ```
352
+
353
+ ## split screen
354
+
355
+ ```tsx
356
+ import { Split } from "@vargai/react";
357
+
358
+ <Clip duration={5}>
359
+ <Split direction="horizontal">
360
+ <Animate
361
+ image={<Image prompt="fat tiger on couch, lazy" />}
362
+ motion="breathing heavily, not moving"
363
+ />
364
+ <Animate
365
+ image={<Image prompt="fat tiger slightly less fat, still on couch" />}
366
+ motion="breathing slightly less heavily"
367
+ />
368
+ </Split>
369
+ <Title position="bottom">WEEK 1 / WEEK 52</Title>
370
+ </Clip>
371
+ ```
372
+
373
+ ## slider (before/after reveal)
374
+
375
+ animated wipe reveal between two images:
376
+
377
+ ```tsx
378
+ import { Slider } from "@vargai/react";
379
+
380
+ <Clip duration={5}>
381
+ <Slider direction="horizontal">
382
+ <Image prompt="luigi standing normally" />
383
+ <Image prompt="luigi in wheelchair, looking defeated" />
384
+ </Slider>
385
+ <Title position="top">What racing does to a mf</Title>
386
+ </Clip>
387
+ ```
388
+
389
+ the slider animates from left to right, revealing the second image.
390
+
391
+ ## swipe animation
392
+
393
+ tinder-style card swipes:
394
+
395
+ ```tsx
396
+ import { Swipe } from "@vargai/react";
397
+
398
+ <Clip duration={6}>
399
+ <Swipe direction="right" interval={1.5}>
400
+ <Image prompt="ralph wiggum dating profile, eating paste" />
401
+ <Image prompt="fat tiger dating profile, lying down" />
402
+ <Image prompt="luigi wheelchair dating profile, sad eyes" />
403
+ <Image prompt="white-teeth guy dating profile, perfect smile" />
404
+ </Swipe>
405
+ </Clip>
406
+ ```
407
+
408
+ ## packshot (end card)
409
+
410
+ end card with call-to-action button:
411
+
412
+ ```tsx
413
+ import { Packshot } from "@vargai/react";
414
+
415
+ <Clip duration={4}>
416
+ <Packshot
417
+ background={<Image prompt="south park style gradient" />}
418
+ logo="./cartman-enterprises.png"
419
+ cta="Respect My Authority"
420
+ ctaColor="#ff5500"
421
+ blinkCta
422
+ />
423
+ </Clip>
424
+ ```
425
+
426
+ ## audio
427
+
428
+ ### background music
429
+
430
+ ```tsx
431
+ <Render>
432
+ {/* loops to match video duration */}
433
+ <Music prompt="epic orchestral, hero music" loop />
434
+
435
+ <Clip duration={5}>
436
+ <Image prompt="luigi wheelchair training montage" />
437
+ </Clip>
438
+
439
+ <Clip duration={5}>
440
+ <Image prompt="luigi wheelchair racing final lap" />
441
+ </Clip>
442
+ </Render>
443
+ ```
444
+
445
+ ### preserve source audio
446
+
447
+ ```tsx
448
+ <Clip>
449
+ <Video src="./interview.mp4" keepAudio volume={0.8} />
450
+ </Clip>
451
+ ```
452
+
453
+ ### mix multiple audio
454
+
455
+ ```tsx
456
+ <Clip duration={10}>
457
+ <Video src="./fat-tiger-sleeping.mp4" keepAudio volume={0.3} />
458
+ <Speech voice="sam" volume={1.0}>
459
+ Day 47. He still hasn't moved. Scientists are baffled.
460
+ </Speech>
461
+ </Clip>
462
+ ```
463
+
464
+ ### audio ducking
465
+
466
+ automatically lower music when speech plays:
467
+
468
+ ```tsx
469
+ <Render>
470
+ <Music prompt="dramatic documentary music" loop ducking />
471
+
472
+ <Clip duration={5}>
473
+ <Image prompt="ralph wiggum walking into frame" />
474
+ </Clip>
475
+
476
+ <Clip duration={10}>
477
+ <Image prompt="ralph wiggum close up, confused" />
478
+ <Speech voice="josh">
479
+ This is ralph. He doesn't know where he is. Neither do we.
480
+ </Speech>
481
+ </Clip>
482
+ </Render>
483
+ ```
484
+
485
+ ### audio normalization
486
+
487
+ ```tsx
488
+ <Render normalize>
489
+ {/* all audio levels balanced automatically */}
490
+ <Clip>
491
+ <Video src="./loud-clip.mp4" keepAudio />
492
+ </Clip>
493
+ <Clip>
494
+ <Video src="./quiet-clip.mp4" keepAudio />
495
+ </Clip>
496
+ </Render>
497
+ ```
498
+
499
+ ## character consistency
500
+
501
+ reuse the same image reference for consistency:
502
+
503
+ ```tsx
504
+ const luigi = <Image prompt="luigi in wheelchair, determined face, mario kart style" />;
505
+
506
+ <Render>
507
+ <Clip duration={3}>
508
+ {luigi}
509
+ <Title>ORIGIN STORY</Title>
510
+ </Clip>
511
+
512
+ <Clip duration={5}>
513
+ <Animate image={luigi} motion="wheelchair wheels spinning, wind in mustache" />
514
+ </Clip>
515
+
516
+ <Clip duration={3}>
517
+ {luigi}
518
+ <Title>HE NEVER RECOVERED</Title>
519
+ </Clip>
520
+ </Render>
521
+ ```
522
+
523
+ same `luigi` reference = same cache key = same generated image.
524
+
525
+ ## elements and scene composition
526
+
527
+ define reusable elements for consistent generation:
528
+
529
+ ```tsx
530
+ import { Element, scene } from "@vargai/react";
531
+
532
+ // define a character element
533
+ const tiger = Element({
534
+ type: "character",
535
+ prompt: "fat tiger, orange stripes, chubby cheeks, sleepy eyes",
536
+ });
537
+
538
+ // define a style element
539
+ const style = Element({
540
+ type: "style",
541
+ prompt: "pixar animation style, soft lighting",
542
+ });
543
+
544
+ // use in scene template
545
+ <Image prompt={scene`${tiger} lying on couch, ${style}`} />
546
+ <Image prompt={scene`${tiger} attempting to exercise, ${style}`} />
547
+ ```
548
+
549
+ elements ensure visual consistency across multiple generations.
550
+
551
+ ## slideshow
552
+
553
+ ```tsx
554
+ const SCENES = [
555
+ "at the gym, confused by equipment",
556
+ "eating entire pizza, no regrets",
557
+ "attempting yoga, stuck",
558
+ "napping on treadmill",
559
+ ];
560
+
561
+ const character = "fat tiger, chubby, adorable";
562
+
563
+ <Render width={1280} height={720}>
564
+ <Music prompt="motivational gym music, ironic" loop />
565
+
566
+ {SCENES.map((scene, i) => (
567
+ <Clip key={i} duration={3} transition={{ name: "fade", duration: 0.3 }}>
568
+ <Image prompt={`${character}, ${scene}`} zoom="in" />
569
+ </Clip>
570
+ ))}
571
+ </Render>
572
+ ```
573
+
574
+ ## character grid
575
+
576
+ ```tsx
577
+ const CHARACTERS = [
578
+ { name: "Ralph", prompt: "ralph wiggum, eating glue, happy" },
579
+ { name: "Fat Tiger", prompt: "fat tiger, lying down, exhausted" },
580
+ { name: "Luigi", prompt: "luigi in wheelchair, sad but determined" },
581
+ { name: "The Smile", prompt: "white-teeth black athletic guy, perfect smile, shiny" },
582
+ ];
583
+
584
+ <Render width={720} height={720}>
585
+ {CHARACTERS.map(({ name, prompt }) => (
586
+ <Clip key={name} duration={2} transition={{ name: "fade", duration: 0.3 }}>
587
+ <Image prompt={`${prompt}, portrait, meme style`} aspectRatio="1:1" />
588
+ <Title position="bottom" color="#ffffff">{name}</Title>
589
+ </Clip>
590
+ ))}
591
+ </Render>
592
+ ```
593
+
594
+ ## conditional rendering
595
+
596
+ standard jsx conditionals:
597
+
598
+ ```tsx
599
+ <Render>
600
+ <Clip duration={5}>
601
+ <Image prompt="south park cartman main scene" />
602
+ </Clip>
603
+
604
+ {includeOutro && (
605
+ <Clip duration={3}>
606
+ <Image prompt="cartman waving goodbye" />
607
+ <Title>Screw you guys!</Title>
608
+ </Clip>
609
+ )}
610
+
611
+ {sponsor && (
612
+ <Clip duration={5}>
613
+ <Image prompt="cartman holding sponsor product" />
614
+ <Speech voice="adam">{sponsor.script}</Speech>
615
+ </Clip>
616
+ )}
617
+ </Render>
618
+ ```
619
+
620
+ ## render options
621
+
622
+ ```tsx
623
+ import { render } from "@vargai/react";
624
+
625
+ // save to file
626
+ await render(<Render>...</Render>, {
627
+ output: "output/video.mp4"
628
+ });
629
+
630
+ // with cache directory
631
+ await render(<Render>...</Render>, {
632
+ output: "output/video.mp4",
633
+ cache: ".cache/ai"
634
+ });
635
+
636
+ // get buffer
637
+ const buffer = await render(<Render>...</Render>);
638
+ await Bun.write("video.mp4", buffer);
639
+
640
+ // stream progress
641
+ const stream = render.stream(<Render>...</Render>);
642
+ for await (const event of stream) {
643
+ console.log(`${event.type}: ${event.progress}%`);
644
+ // "generating image: 45%"
645
+ // "generating speech: 100%"
646
+ // "rendering clip: 30%"
647
+ }
648
+ ```
649
+
650
+ ## full example: ralph explains crypto
651
+
652
+ ```tsx
653
+ import { render, Render, Clip, Image, Animate, Speech, Title } from "@vargai/react";
654
+ import { fal, elevenlabs } from "@vargai/sdk";
655
+
656
+ // TalkingHead is just a composition of primitives
657
+ const TalkingHead = ({ character, voice, children }: {
658
+ character: string;
659
+ voice: string;
660
+ children: string;
661
+ }) => (
662
+ <Clip duration="auto">
663
+ <Animate
664
+ image={<Image prompt={character} model={fal.imageModel("flux-schnell")} />}
665
+ model={fal.videoModel("wan-2.5")}
666
+ motion="subtle head movements, blinking, mouth moving"
667
+ />
668
+ <Speech voice={voice} model={elevenlabs.speechModel("turbo")}>
669
+ {children}
670
+ </Speech>
671
+ </Clip>
672
+ );
673
+
674
+ const script = `Hi, I'm Ralph! My cat's breath smells like cat food.
675
+ Also I invested my lunch money in dogecoin.
676
+ Now I live in a box. But it's a nice box!
677
+ It has a window. The window is a hole.`;
678
+
679
+ await render(
680
+ <Render width={1080} height={1920}>
681
+ <TalkingHead
682
+ character="ralph wiggum, simpsons style, innocent smile, slightly confused"
683
+ voice="adam"
684
+ >
685
+ {script}
686
+ </TalkingHead>
687
+
688
+ <Clip duration={2} transition={{ name: "fade", duration: 0.5 }}>
689
+ <Image
690
+ prompt="ralph wiggum in cardboard box, happy, simpsons style"
691
+ model={fal.imageModel("flux-schnell")}
692
+ zoom="in"
693
+ />
694
+ <Title position="bottom">@RalphInvests</Title>
695
+ </Clip>
696
+ </Render>,
697
+ {
698
+ output: "output/ralph-crypto.mp4",
699
+ cache: ".cache/ai"
700
+ }
701
+ );
702
+ ```
703
+
704
+ ## full example: wheelchair racing promo
705
+
706
+ ```tsx
707
+ import { render, Render, Clip, Image, Animate, Title, Subtitle, Music } from "@vargai/react";
708
+
709
+ const luigi = <Image prompt="luigi in racing wheelchair, determined, mario kart style" />;
710
+
711
+ await render(
712
+ <Render width={1080} height={1920}>
713
+ <Music prompt="epic racing music, fast drums, intense" loop />
714
+
715
+ <Clip duration={3}>
716
+ {luigi}
717
+ <Title start={1}>This Summer</Title>
718
+ </Clip>
719
+
720
+ <Clip duration={5} transition={{ name: "fade", duration: 0.5 }}>
721
+ <Animate
722
+ image={luigi}
723
+ motion="wheelchair wheels spinning fast, wind effects, speed lines"
724
+ />
725
+ <Title position="bottom">NO LIMITS</Title>
726
+ </Clip>
727
+
728
+ <Clip duration={3} transition={{ name: "fade", duration: 0.5 }}>
729
+ {luigi}
730
+ <Title>LUIGI KART: WHEELCHAIR EDITION</Title>
731
+ <Subtitle>He can't walk but he can win</Subtitle>
732
+ </Clip>
733
+ </Render>,
734
+ { output: "output/luigi-promo.mp4" }
735
+ );
736
+ ```
737
+
738
+ ## full example: fat tiger's fitness journey
739
+
740
+ ```tsx
741
+ import { render, Render, Clip, Image, Animate, Split, Title } from "@vargai/react";
742
+
743
+ const tiger = "fat tiger, orange stripes, cute, pixar style";
744
+
745
+ const before = <Image prompt={`${tiger}, extremely chubby, on couch, pizza boxes`} aspectRatio="3:4" />;
746
+ const after = <Image prompt={`${tiger}, slightly less chubby, still on couch, salad nearby unopened`} aspectRatio="3:4" />;
747
+
748
+ await render(
749
+ <Render width={1280} height={720}>
750
+ <Clip duration={5}>
751
+ <Split direction="horizontal">
752
+ <Animate image={before} motion="breathing heavily, belly jiggles" />
753
+ <Animate image={after} motion="breathing slightly less heavily, one ear twitches" />
754
+ </Split>
755
+ <Title position="bottom" color="#ffffff">
756
+ DAY 1 DAY 365
757
+ </Title>
758
+ </Clip>
759
+ </Render>,
760
+ { output: "output/tiger-transformation.mp4" }
761
+ );
762
+ ```
763
+
764
+ ## models
765
+
766
+ specify which ai model to use:
767
+
768
+ ```tsx
769
+ import { Image, Animate, Speech, TalkingHead } from "@vargai/react";
770
+ import { fal, openai, replicate, elevenlabs, higgsfield } from "@vargai/sdk";
771
+
772
+ // image models
773
+ <Image prompt="sunset" model={fal.imageModel("flux-schnell")} />
774
+ <Image prompt="sunset" model={fal.imageModel("flux-pro")} />
775
+ <Image prompt="sunset" model={openai.imageModel("dall-e-3")} />
776
+ <Image prompt="sunset" model={replicate.imageModel("sdxl")} />
777
+
778
+ // video models
779
+ <Animate model={fal.videoModel("kling-v2.5")} motion="slow zoom" />
780
+ <Animate model={fal.videoModel("wan-2.5")} motion="camera pan" />
781
+ <Animate model={higgsfield.videoModel("soul")} motion="walking" />
782
+
783
+ // speech models
784
+ <Speech model={elevenlabs.speechModel("turbo")} voice="rachel">
785
+ hello world
786
+ </Speech>
787
+
788
+ // talking head with lipsync
789
+ <TalkingHead
790
+ model={fal.videoModel("wan-2.5")}
791
+ lipsyncModel={fal.videoModel("sync-v2")}
792
+ voice="adam"
793
+ >
794
+ this syncs lips to speech
795
+ </TalkingHead>
796
+ ```
797
+
798
+ ### higgsfield characters
799
+
800
+ for consistent character animation:
801
+
802
+ ```tsx
803
+ <Render>
804
+ <Clip duration={5}>
805
+ <Animate
806
+ model={higgsfield.videoModel("soul")}
807
+ character="white-teeth-guy"
808
+ motion="walking forward with perfect posture, teeth gleaming"
809
+ />
810
+ </Clip>
811
+
812
+ <Clip duration={5}>
813
+ <Animate
814
+ model={higgsfield.videoModel("soul")}
815
+ character="white-teeth-guy"
816
+ motion="pointing at camera, smile intensifies"
817
+ />
818
+ </Clip>
819
+ </Render>
820
+ ```
821
+
822
+ same `character` id = consistent appearance across clips.
823
+
824
+ ## why varg-react?
825
+
826
+ | imperative (current) | declarative (varg-react) |
827
+ |---------------------|-------------------------|
828
+ | manual cache keys | automatic from props |
829
+ | step-by-step generation | parallel where possible |
830
+ | explicit file handling | automatic temp files |
831
+ | editly config objects | jsx composition |
832
+ | ~50 lines for talking head | ~10 lines |
833
+
834
+ same power, less code, automatic caching.