mulmocast 2.6.4 → 2.6.6

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 (39) hide show
  1. package/lib/actions/graph_option.d.ts +3 -0
  2. package/lib/actions/graph_option.js +18 -0
  3. package/lib/actions/image_references.d.ts +1 -0
  4. package/lib/actions/image_references.js +50 -12
  5. package/lib/actions/images.d.ts +2 -3
  6. package/lib/actions/images.js +4 -17
  7. package/lib/actions/movie.d.ts +3 -0
  8. package/lib/actions/movie.js +38 -5
  9. package/lib/agents/add_bgm_agent.d.ts +10 -0
  10. package/lib/agents/add_bgm_agent.js +26 -4
  11. package/lib/agents/movie_genai_agent.js +1 -1
  12. package/lib/agents/movie_replicate_agent.js +29 -5
  13. package/lib/types/provider2agent.d.ts +2 -0
  14. package/lib/types/provider2agent.js +78 -5
  15. package/lib/types/schema.d.ts +166 -4
  16. package/lib/types/schema.js +11 -1
  17. package/lib/utils/context.d.ts +72 -2
  18. package/lib/utils/image_plugins/html_tailwind.d.ts +5 -0
  19. package/lib/utils/image_plugins/html_tailwind.js +56 -5
  20. package/package.json +13 -12
  21. package/scripts/test/fixtures/movie_tone_high.mov +0 -0
  22. package/scripts/test/fixtures/movie_tone_low.mov +0 -0
  23. package/scripts/test/fixtures/movie_tone_mid.mov +0 -0
  24. package/scripts/test/glb/sample_2026-03-15T172907.296_compat.glb +0 -0
  25. package/scripts/test/test_audio_mix.json +91 -0
  26. package/scripts/test/test_audio_mix_beat_vol.json +100 -0
  27. package/scripts/test/test_audio_mix_ducking.json +91 -0
  28. package/scripts/test/test_audio_mix_legacy.json +90 -0
  29. package/scripts/test/test_grok.json +57 -0
  30. package/scripts/test/test_image_prompt_reference.json +55 -0
  31. package/scripts/test/test_image_references.json +74 -0
  32. package/scripts/test/test_kling_v3.json +54 -0
  33. package/scripts/test/test_kling_v3_omni.json +54 -0
  34. package/scripts/test/test_lipsync2.json +48 -52
  35. package/scripts/test/test_lipsync5.json +66 -0
  36. package/scripts/test/test_runway.json +54 -0
  37. package/scripts/test/test_threejs.json +241 -0
  38. package/scripts/test/test_threejs_glb.json +154 -0
  39. package/scripts/test/test_veo31_lite.json +39 -0
@@ -1,66 +1,62 @@
1
1
  {
2
- "$mulmocast": {
3
- "version": "1.1",
4
- "credit": "closing"
5
- },
6
- "canvasSize": {
7
- "width": 1280,
8
- "height": 720
2
+ "$mulmocast": { "version": "1.1" },
3
+ "lipSyncParams": {
4
+ "provider": "replicate",
5
+ "model": "bytedance/latentsync"
9
6
  },
10
- "speechParams": {
11
- "speakers": {
12
- "Presenter": {
13
- "displayName": {
14
- "en": "Presenter"
15
- },
16
- "voiceId": "shimmer",
17
- "provider": "openai"
18
- }
19
- }
7
+ "audioParams": {
8
+ "bgmVolume": 0.01,
9
+ "introPadding": 0,
10
+ "padding": 0.0
20
11
  },
21
12
  "imageParams": {
22
- "provider": "openai",
23
13
  "images": {
24
- "woman": {
25
- "type": "image",
26
- "source": {
27
- "kind": "path",
28
- "path": "./image-2.png"
29
- }
14
+ "presenter": {
15
+ "type": "imagePrompt",
16
+ "prompt": "A professional female presenter standing in front of a modern presentation screen, speaking confidently, photo realistic, front-facing, neutral expression, well-lit"
30
17
  }
31
18
  }
32
19
  },
33
- "movieParams": {
34
- "provider": "replicate",
35
- "model": "bytedance/seedance-1-lite"
36
- },
37
- "soundEffectParams": {
38
- "provider": "replicate"
39
- },
40
- "lipSyncParams": {
41
- "provider": "replicate",
42
- "model": "bytedance/latentsync"
43
- },
44
- "audioParams": {
45
- "padding": 0.3,
46
- "introPadding": 1,
47
- "closingPadding": 0.8,
48
- "outroPadding": 1,
49
- "bgmVolume": 0.2,
50
- "audioVolume": 1,
51
- "suppressSpeech": false
52
- },
53
- "title": "リップシンク(generate contents)",
54
- "description": "mulmocast",
55
20
  "lang": "en",
56
21
  "beats": [
57
22
  {
58
- "speaker": "Presenter",
59
- "text": "You've had a challenging day. The evidence is clear: you need rest. You're more capable than you realize. Case closed. hello hello !!",
60
- "id": "c0e344e1-4ed7-4145-b7fc-c878c093cffd",
61
- "imagePrompt": "in the city",
62
- "moviePrompt": "never looking away from camera",
63
- "enableLipSync": true
23
+ "id": "latentsync",
24
+ "text": "Artificial intelligence is transforming the world around us, enabling machines to learn, adapt, and solve complex problems in ways we never imagined possible.",
25
+ "imageNames": ["presenter"],
26
+ "moviePrompt": "A professional female presenter speaking in front of a modern presentation screen",
27
+ "enableLipSync": true,
28
+ "lipSyncParams": {
29
+ "model": "bytedance/latentsync"
30
+ }
31
+ },
32
+ {
33
+ "id": "tmappdev_lipsync",
34
+ "text": "Artificial intelligence is transforming the world around us, enabling machines to learn, adapt, and solve complex problems in ways we never imagined possible.",
35
+ "imageNames": ["presenter"],
36
+ "moviePrompt": "A professional female presenter speaking in front of a modern presentation screen",
37
+ "enableLipSync": true,
38
+ "lipSyncParams": {
39
+ "model": "tmappdev/lipsync"
40
+ }
41
+ },
42
+ {
43
+ "id": "omni_human",
44
+ "text": "Artificial intelligence is transforming the world around us.",
45
+ "imageNames": ["presenter"],
46
+ "enableLipSync": true,
47
+ "lipSyncParams": {
48
+ "model": "bytedance/omni-human"
49
+ }
50
+ },
51
+ {
52
+ "id": "pixverse_lipsync",
53
+ "text": "Artificial intelligence is transforming the world around us, enabling machines to learn, adapt, and solve complex problems in ways we never imagined possible.",
54
+ "imageNames": ["presenter"],
55
+ "moviePrompt": "A professional female presenter speaking in front of a modern presentation screen",
56
+ "enableLipSync": true,
57
+ "lipSyncParams": {
58
+ "model": "pixverse/lipsync"
59
+ }
64
60
  }
65
61
  ]
66
62
  }
@@ -0,0 +1,66 @@
1
+ {
2
+ "$mulmocast": {
3
+ "version": "1.1",
4
+ "credit": "closing"
5
+ },
6
+ "canvasSize": {
7
+ "width": 1280,
8
+ "height": 720
9
+ },
10
+ "speechParams": {
11
+ "speakers": {
12
+ "Presenter": {
13
+ "displayName": {
14
+ "en": "Presenter"
15
+ },
16
+ "voiceId": "shimmer",
17
+ "provider": "openai"
18
+ }
19
+ }
20
+ },
21
+ "imageParams": {
22
+ "provider": "openai",
23
+ "images": {
24
+ "woman": {
25
+ "type": "image",
26
+ "source": {
27
+ "kind": "path",
28
+ "path": "./image-2.png"
29
+ }
30
+ }
31
+ }
32
+ },
33
+ "movieParams": {
34
+ "provider": "replicate",
35
+ "model": "bytedance/seedance-1-lite"
36
+ },
37
+ "soundEffectParams": {
38
+ "provider": "replicate"
39
+ },
40
+ "lipSyncParams": {
41
+ "provider": "replicate",
42
+ "model": "bytedance/latentsync"
43
+ },
44
+ "audioParams": {
45
+ "padding": 0.3,
46
+ "introPadding": 1,
47
+ "closingPadding": 0.8,
48
+ "outroPadding": 1,
49
+ "bgmVolume": 0.2,
50
+ "audioVolume": 1,
51
+ "suppressSpeech": false
52
+ },
53
+ "title": "リップシンク(generate contents)",
54
+ "description": "mulmocast",
55
+ "lang": "en",
56
+ "beats": [
57
+ {
58
+ "speaker": "Presenter",
59
+ "text": "You've had a challenging day. The evidence is clear: you need rest. You're more capable than you realize. Case closed. hello hello !!",
60
+ "id": "c0e344e1-4ed7-4145-b7fc-c878c093cffd",
61
+ "imagePrompt": "in the city",
62
+ "moviePrompt": "never looking away from camera",
63
+ "enableLipSync": true
64
+ }
65
+ ]
66
+ }
@@ -0,0 +1,54 @@
1
+ {
2
+ "$mulmocast": { "version": "1.1" },
3
+ "movieParams": {
4
+ "provider": "replicate",
5
+ "model": "runwayml/gen-4.5"
6
+ },
7
+ "imageParams": {
8
+ "provider": "google"
9
+ },
10
+ "audioParams": {
11
+ "bgmVolume": 0
12
+ },
13
+ "captionParams": {
14
+ "lang": "en"
15
+ },
16
+ "lang": "en",
17
+ "beats": [
18
+ {
19
+ "text": "Testing runwayml/gen-4.5 — text-to-video and image-to-video varieties.",
20
+ "image": {
21
+ "type": "textSlide",
22
+ "slide": {
23
+ "title": "runwayml/gen-4.5 test"
24
+ }
25
+ }
26
+ },
27
+ {
28
+ "id": "runway-t2v-5s",
29
+ "text": "Text-to-video, 5 seconds: a red fox trots through a snowy forest at dawn, pawprints left behind",
30
+ "duration": 5,
31
+ "moviePrompt": "a red fox trots through a snowy forest at dawn, pawprints left behind, soft golden light filtering through pine trees"
32
+ },
33
+ {
34
+ "id": "runway-t2v-10s",
35
+ "text": "Text-to-video, 10 seconds: bustling Tokyo crossing at night",
36
+ "duration": 10,
37
+ "moviePrompt": "aerial dolly shot over Shibuya crossing at night, neon lights reflecting on wet pavement, crowds of people crossing in all directions"
38
+ },
39
+ {
40
+ "id": "runway-i2v-5s",
41
+ "text": "Image-to-video, 5 seconds: ocean waves animated from a still image",
42
+ "duration": 5,
43
+ "imagePrompt": "a calm tropical beach at sunset, crystal-clear turquoise water, golden sand, photorealistic",
44
+ "moviePrompt": "gentle waves lapping the shore, palm trees swaying softly in the breeze, camera slowly pushing forward"
45
+ },
46
+ {
47
+ "id": "runway-i2v-10s",
48
+ "text": "Image-to-video, 10 seconds: city skyline coming to life",
49
+ "duration": 10,
50
+ "imagePrompt": "futuristic city skyline at dusk, glass skyscrapers with glowing windows, photorealistic",
51
+ "moviePrompt": "lights in the skyscraper windows flicker on one by one as night falls, slow upward pan along the tallest tower"
52
+ }
53
+ ]
54
+ }
@@ -0,0 +1,241 @@
1
+ {
2
+ "$mulmocast": {
3
+ "version": "1.1"
4
+ },
5
+ "lang": "ja",
6
+ "title": "Three.js 3D Objects Demo",
7
+ "canvasSize": { "width": 1280, "height": 720 },
8
+ "speechParams": {
9
+ "speakers": {
10
+ "narrator": {
11
+ "voiceId": "nova",
12
+ "displayName": { "ja": "ナレーター" }
13
+ }
14
+ }
15
+ },
16
+ "audioParams": {
17
+ "padding": 0,
18
+ "introPadding": 0,
19
+ "closingPadding": 0,
20
+ "outroPadding": 0
21
+ },
22
+ "beats": [
23
+ {
24
+ "id": "cube_rotate",
25
+ "speaker": "narrator",
26
+ "text": "まずは立方体。ゆっくり回転しています。",
27
+ "duration": 6,
28
+ "image": {
29
+ "type": "html_tailwind",
30
+ "html": [
31
+ "<div id='three-container' style='position:fixed;inset:0;overflow:hidden;background:#111122'>",
32
+ " <canvas id='c' style='display:block;width:100%;height:100%'></canvas>",
33
+ "</div>"
34
+ ],
35
+ "script": [
36
+ "import * as THREE from 'https://cdn.jsdelivr.net/npm/three@0.182.0/build/three.module.js';",
37
+ "const canvas = document.getElementById('c');",
38
+ "const renderer = new THREE.WebGLRenderer({ canvas, antialias: true, preserveDrawingBuffer: true });",
39
+ "renderer.setClearColor(0x1a1a2e);",
40
+ "renderer.outputColorSpace = THREE.SRGBColorSpace;",
41
+ "",
42
+ "const scene = new THREE.Scene();",
43
+ "const camera = new THREE.PerspectiveCamera(50, 16/9, 0.1, 100);",
44
+ "camera.position.set(0, 1.5, 4);",
45
+ "camera.lookAt(0, 0, 0);",
46
+ "",
47
+ "scene.add(new THREE.AmbientLight(0x404040, 2));",
48
+ "const dirLight = new THREE.DirectionalLight(0xffffff, 3);",
49
+ "dirLight.position.set(3, 5, 4);",
50
+ "scene.add(dirLight);",
51
+ "",
52
+ "const cube = new THREE.Mesh(",
53
+ " new THREE.BoxGeometry(1.5, 1.5, 1.5),",
54
+ " new THREE.MeshStandardMaterial({ color: 0x00d2ff, metalness: 0.3, roughness: 0.4 })",
55
+ ");",
56
+ "scene.add(cube);",
57
+ "",
58
+ "let lastW = 0;",
59
+ "let lastH = 0;",
60
+ "const syncSize = () => {",
61
+ " const w = Math.max(1, Math.floor(window.innerWidth || document.documentElement.clientWidth));",
62
+ " const h = Math.max(1, Math.floor(window.innerHeight || document.documentElement.clientHeight));",
63
+ " if (w !== lastW || h !== lastH) {",
64
+ " renderer.setSize(w, h, true);",
65
+ " camera.aspect = w / h;",
66
+ " camera.updateProjectionMatrix();",
67
+ " lastW = w;",
68
+ " lastH = h;",
69
+ " }",
70
+ "};",
71
+ "window.render = function(frame, total) {",
72
+ " document.body.style.zoom = '1';",
73
+ " syncSize();",
74
+ " const t = frame / total;",
75
+ " cube.rotation.x = t * Math.PI * 2;",
76
+ " cube.rotation.y = t * Math.PI * 3;",
77
+ " renderer.render(scene, camera);",
78
+ "};"
79
+ ],
80
+ "animation": { "fps": 30 }
81
+ }
82
+ },
83
+ {
84
+ "id": "crystal_spin",
85
+ "speaker": "narrator",
86
+ "text": "次はクリスタル。光を受けてキラキラ輝きます。",
87
+ "duration": 6,
88
+ "image": {
89
+ "type": "html_tailwind",
90
+ "html": [
91
+ "<div id='three-container' style='position:fixed;inset:0;overflow:hidden;background:#111122'>",
92
+ " <canvas id='c' style='display:block;width:100%;height:100%'></canvas>",
93
+ "</div>"
94
+ ],
95
+ "script": [
96
+ "import * as THREE from 'https://cdn.jsdelivr.net/npm/three@0.182.0/build/three.module.js';",
97
+ "const canvas = document.getElementById('c');",
98
+ "const renderer = new THREE.WebGLRenderer({ canvas, antialias: true, preserveDrawingBuffer: true });",
99
+ "renderer.setClearColor(0x0d0d1a);",
100
+ "renderer.outputColorSpace = THREE.SRGBColorSpace;",
101
+ "",
102
+ "const scene = new THREE.Scene();",
103
+ "const camera = new THREE.PerspectiveCamera(50, 16/9, 0.1, 100);",
104
+ "camera.position.set(0, 2, 5);",
105
+ "camera.lookAt(0, 0, 0);",
106
+ "",
107
+ "scene.add(new THREE.AmbientLight(0x334455, 3));",
108
+ "const pointLight1 = new THREE.PointLight(0xff66aa, 2, 20);",
109
+ "pointLight1.position.set(3, 3, 3);",
110
+ "scene.add(pointLight1);",
111
+ "const pointLight2 = new THREE.PointLight(0x66aaff, 2, 20);",
112
+ "pointLight2.position.set(-3, 2, -2);",
113
+ "scene.add(pointLight2);",
114
+ "",
115
+ "const crystal = new THREE.Mesh(",
116
+ " new THREE.OctahedronGeometry(1.2, 0),",
117
+ " new THREE.MeshStandardMaterial({ color: 0x88ccff, metalness: 0.6, roughness: 0.1 })",
118
+ ");",
119
+ "crystal.scale.set(1, 1.6, 1);",
120
+ "scene.add(crystal);",
121
+ "",
122
+ "let lastW = 0;",
123
+ "let lastH = 0;",
124
+ "const syncSize = () => {",
125
+ " const w = Math.max(1, Math.floor(window.innerWidth || document.documentElement.clientWidth));",
126
+ " const h = Math.max(1, Math.floor(window.innerHeight || document.documentElement.clientHeight));",
127
+ " if (w !== lastW || h !== lastH) {",
128
+ " renderer.setSize(w, h, true);",
129
+ " camera.aspect = w / h;",
130
+ " camera.updateProjectionMatrix();",
131
+ " lastW = w;",
132
+ " lastH = h;",
133
+ " }",
134
+ "};",
135
+ "window.render = function(frame, total) {",
136
+ " document.body.style.zoom = '1';",
137
+ " syncSize();",
138
+ " const t = frame / total;",
139
+ " crystal.rotation.y = t * Math.PI * 4;",
140
+ " crystal.rotation.x = Math.sin(t * Math.PI * 2) * 0.3;",
141
+ " pointLight1.position.x = Math.cos(t * Math.PI * 4) * 4;",
142
+ " pointLight1.position.z = Math.sin(t * Math.PI * 4) * 4;",
143
+ " pointLight2.position.x = Math.cos(t * Math.PI * 3 + 2) * 3;",
144
+ " pointLight2.position.z = Math.sin(t * Math.PI * 3 + 2) * 3;",
145
+ " renderer.render(scene, camera);",
146
+ "};"
147
+ ],
148
+ "animation": { "fps": 30 }
149
+ }
150
+ },
151
+ {
152
+ "id": "both_objects",
153
+ "speaker": "narrator",
154
+ "text": "最後に両方のオブジェクトを並べて。立方体とクリスタルが一緒に回ります。",
155
+ "duration": 8,
156
+ "image": {
157
+ "type": "html_tailwind",
158
+ "html": [
159
+ "<div id='three-container' style='position:fixed;inset:0;overflow:hidden;background:#111122'>",
160
+ " <canvas id='c' style='display:block;width:100%;height:100%'></canvas>",
161
+ "</div>"
162
+ ],
163
+ "script": [
164
+ "import * as THREE from 'https://cdn.jsdelivr.net/npm/three@0.182.0/build/three.module.js';",
165
+ "const canvas = document.getElementById('c');",
166
+ "const renderer = new THREE.WebGLRenderer({ canvas, antialias: true, preserveDrawingBuffer: true });",
167
+ "renderer.setClearColor(0x111122);",
168
+ "renderer.outputColorSpace = THREE.SRGBColorSpace;",
169
+ "",
170
+ "const scene = new THREE.Scene();",
171
+ "const camera = new THREE.PerspectiveCamera(50, 16/9, 0.1, 100);",
172
+ "camera.position.set(0, 2, 6);",
173
+ "camera.lookAt(0, 0, 0);",
174
+ "",
175
+ "scene.add(new THREE.AmbientLight(0x404060, 2));",
176
+ "const dirLight = new THREE.DirectionalLight(0xffffff, 2);",
177
+ "dirLight.position.set(3, 5, 4);",
178
+ "scene.add(dirLight);",
179
+ "const pointLight1 = new THREE.PointLight(0xff6688, 2, 20);",
180
+ "pointLight1.position.set(4, 3, 3);",
181
+ "scene.add(pointLight1);",
182
+ "const pointLight2 = new THREE.PointLight(0x6688ff, 2, 20);",
183
+ "pointLight2.position.set(-4, 2, -2);",
184
+ "scene.add(pointLight2);",
185
+ "",
186
+ "const cube = new THREE.Mesh(",
187
+ " new THREE.BoxGeometry(1.2, 1.2, 1.2),",
188
+ " new THREE.MeshStandardMaterial({ color: 0x00d2ff, metalness: 0.3, roughness: 0.4 })",
189
+ ");",
190
+ "cube.position.x = -2;",
191
+ "scene.add(cube);",
192
+ "",
193
+ "const crystal = new THREE.Mesh(",
194
+ " new THREE.OctahedronGeometry(1.0, 0),",
195
+ " new THREE.MeshStandardMaterial({ color: 0xcc88ff, metalness: 0.6, roughness: 0.1 })",
196
+ ");",
197
+ "crystal.scale.set(1, 1.5, 1);",
198
+ "crystal.position.x = 2;",
199
+ "scene.add(crystal);",
200
+ "",
201
+ "const grid = new THREE.GridHelper(10, 20, 0x444466, 0x222244);",
202
+ "grid.position.y = -1.5;",
203
+ "scene.add(grid);",
204
+ "",
205
+ "let lastW = 0;",
206
+ "let lastH = 0;",
207
+ "const syncSize = () => {",
208
+ " const w = Math.max(1, Math.floor(window.innerWidth || document.documentElement.clientWidth));",
209
+ " const h = Math.max(1, Math.floor(window.innerHeight || document.documentElement.clientHeight));",
210
+ " if (w !== lastW || h !== lastH) {",
211
+ " renderer.setSize(w, h, true);",
212
+ " camera.aspect = w / h;",
213
+ " camera.updateProjectionMatrix();",
214
+ " lastW = w;",
215
+ " lastH = h;",
216
+ " }",
217
+ "};",
218
+ "window.render = function(frame, total) {",
219
+ " document.body.style.zoom = '1';",
220
+ " syncSize();",
221
+ " const t = frame / total;",
222
+ " cube.rotation.x = t * Math.PI * 2;",
223
+ " cube.rotation.y = t * Math.PI * 3;",
224
+ " cube.position.y = Math.sin(t * Math.PI * 4) * 0.5;",
225
+ " crystal.rotation.y = t * Math.PI * 4;",
226
+ " crystal.rotation.x = Math.sin(t * Math.PI * 2) * 0.3;",
227
+ " crystal.position.y = Math.cos(t * Math.PI * 3) * 0.4 + 0.3;",
228
+ " pointLight1.position.x = Math.cos(t * Math.PI * 4) * 4;",
229
+ " pointLight1.position.z = Math.sin(t * Math.PI * 4) * 4;",
230
+ " pointLight2.position.x = Math.cos(t * Math.PI * 3 + 2) * 3;",
231
+ " pointLight2.position.z = Math.sin(t * Math.PI * 3 + 2) * 3;",
232
+ " camera.position.x = Math.sin(t * Math.PI * 2) * 1.5;",
233
+ " camera.lookAt(0, 0, 0);",
234
+ " renderer.render(scene, camera);",
235
+ "};"
236
+ ],
237
+ "animation": { "fps": 30 }
238
+ }
239
+ }
240
+ ]
241
+ }
@@ -0,0 +1,154 @@
1
+ {
2
+ "$mulmocast": {
3
+ "version": "1.1"
4
+ },
5
+ "lang": "ja",
6
+ "title": "Three.js GLB Demo",
7
+ "canvasSize": {
8
+ "width": 1280,
9
+ "height": 720
10
+ },
11
+ "audioParams": {
12
+ "padding": 0,
13
+ "introPadding": 0,
14
+ "closingPadding": 0,
15
+ "outroPadding": 0
16
+ },
17
+ "beats": [
18
+ {
19
+ "id": "glb_preview",
20
+ "duration": 16,
21
+ "text": "GLBモデルのプレビューです。",
22
+ "image": {
23
+ "type": "html_tailwind",
24
+ "html": [
25
+ "<div id='three-container' style='position:fixed;inset:0;overflow:hidden;background:#0d1224'>",
26
+ " <canvas id='c' style='display:block;width:100%;height:100%'></canvas>",
27
+ "</div>"
28
+ ],
29
+ "script": [
30
+ "import * as THREE from 'https://esm.sh/three@0.182.0';",
31
+ "import { GLTFLoader } from 'https://esm.sh/three@0.182.0/examples/jsm/loaders/GLTFLoader.js';",
32
+ "const canvas = document.getElementById('c');",
33
+ "const renderer = new THREE.WebGLRenderer({ canvas, antialias: true, preserveDrawingBuffer: true });",
34
+ "renderer.setClearColor(0x0d1224);",
35
+ "renderer.outputColorSpace = THREE.SRGBColorSpace;",
36
+ "const scene = new THREE.Scene();",
37
+ "const camera = new THREE.PerspectiveCamera(45, 16 / 9, 0.01, 1000);",
38
+ "camera.position.set(0, 1.2, 7.0);",
39
+ "camera.lookAt(0, 0.9, 0);",
40
+ "camera.zoom = 1.0;",
41
+ "camera.updateProjectionMatrix();",
42
+ "scene.add(new THREE.AmbientLight(0xffffff, 1.2));",
43
+ "const key = new THREE.DirectionalLight(0xffffff, 1.4);",
44
+ "key.position.set(3, 5, 2);",
45
+ "scene.add(key);",
46
+ "const rim = new THREE.DirectionalLight(0x88aaff, 0.8);",
47
+ "rim.position.set(-4, 2, -3);",
48
+ "scene.add(rim);",
49
+ "const grid = new THREE.GridHelper(10, 20, 0x444466, 0x222244);",
50
+ "grid.position.y = -0.01;",
51
+ "scene.add(grid);",
52
+ "let moveRoot = null;",
53
+ "let turnRoot = null;",
54
+ "let modelOffset = null;",
55
+ "let modelReadyResolved = false;",
56
+ "let resolveModelReady;",
57
+ "const modelReady = new Promise((resolve) => { resolveModelReady = resolve; });",
58
+ "const modelUrl = 'glb/sample_2026-03-15T172907.296_compat.glb';",
59
+ "const loader = new GLTFLoader();",
60
+ "loader.load(modelUrl, (gltf) => {",
61
+ " moveRoot = new THREE.Group();",
62
+ " turnRoot = new THREE.Group();",
63
+ " modelOffset = new THREE.Group();",
64
+ " moveRoot.add(turnRoot);",
65
+ " turnRoot.add(modelOffset);",
66
+ " modelOffset.add(gltf.scene);",
67
+ " scene.add(moveRoot);",
68
+ " const box = new THREE.Box3().setFromObject(modelOffset);",
69
+ " const size = box.getSize(new THREE.Vector3());",
70
+ " const maxDim = Math.max(size.x, size.y, size.z) || 1;",
71
+ " const scale = 1.8 / maxDim;",
72
+ " modelOffset.scale.setScalar(scale);",
73
+ " box.setFromObject(modelOffset);",
74
+ " const centered = box.getCenter(new THREE.Vector3());",
75
+ " modelOffset.position.sub(centered);",
76
+ " modelOffset.position.y += 0.9;",
77
+ " modelReadyResolved = true;",
78
+ " resolveModelReady();",
79
+ "}, undefined, (error) => {",
80
+ " console.error('GLB load failed:', error);",
81
+ " modelReadyResolved = true;",
82
+ " resolveModelReady();",
83
+ "});",
84
+ "let lastW = 0;",
85
+ "let lastH = 0;",
86
+ "const syncSize = () => {",
87
+ " const w = Math.max(1, Math.floor(window.innerWidth || document.documentElement.clientWidth));",
88
+ " const h = Math.max(1, Math.floor(window.innerHeight || document.documentElement.clientHeight));",
89
+ " if (w !== lastW || h !== lastH) {",
90
+ " renderer.setSize(w, h, true);",
91
+ " camera.aspect = w / h;",
92
+ " camera.updateProjectionMatrix();",
93
+ " lastW = w;",
94
+ " lastH = h;",
95
+ " }",
96
+ "};",
97
+ "const lerp = (a, b, u) => a + (b - a) * u;",
98
+ "const clamp01 = (v) => Math.max(0, Math.min(1, v));",
99
+ "const xMin = -1.6;",
100
+ "const xMax = 1.6;",
101
+ "const moveEnd = 0.45;",
102
+ "const turnEnd = 0.55;",
103
+ "const turnFrontEnd = moveEnd + (turnEnd - moveEnd) * 0.75;",
104
+ "const faceRight = Math.PI / 2;",
105
+ "const faceFront = 0;",
106
+ "const faceLeft = -Math.PI / 2;",
107
+ "const holdFrontSeconds = 3;",
108
+ "window.render = async function render(frame, total, fps) {",
109
+ " if (!modelReadyResolved) {",
110
+ " await modelReady;",
111
+ " }",
112
+ " document.body.style.zoom = '1';",
113
+ " syncSize();",
114
+ " const t = frame / Math.max(1, total - 1);",
115
+ " camera.zoom = lerp(1.0, 1.18, t);",
116
+ " camera.updateProjectionMatrix();",
117
+ " const fpsSafe = fps || 30;",
118
+ " const holdFrontSpan = (holdFrontSeconds * fpsSafe) / Math.max(1, total - 1);",
119
+ " const holdFrontEnd = Math.min(0.97, turnFrontEnd + holdFrontSpan);",
120
+ " const rotateLeftSpan = Math.max(0.02, turnEnd - turnFrontEnd);",
121
+ " const rotateLeftEnd = Math.min(0.985, holdFrontEnd + rotateLeftSpan);",
122
+ " if (moveRoot && turnRoot) {",
123
+ " if (t < moveEnd) {",
124
+ " const u = clamp01(t / moveEnd);",
125
+ " moveRoot.position.x = lerp(xMin, xMax, u);",
126
+ " turnRoot.rotation.y = faceRight;",
127
+ " } else if (t < turnFrontEnd) {",
128
+ " const u = clamp01((t - moveEnd) / Math.max(1e-6, turnFrontEnd - moveEnd));",
129
+ " moveRoot.position.x = xMax;",
130
+ " turnRoot.rotation.y = lerp(faceRight, faceFront, u);",
131
+ " } else if (t < holdFrontEnd) {",
132
+ " moveRoot.position.x = xMax;",
133
+ " turnRoot.rotation.y = faceFront;",
134
+ " } else if (t < rotateLeftEnd) {",
135
+ " const u = clamp01((t - holdFrontEnd) / Math.max(1e-6, rotateLeftEnd - holdFrontEnd));",
136
+ " moveRoot.position.x = xMax;",
137
+ " turnRoot.rotation.y = lerp(faceFront, faceLeft, u);",
138
+ " } else {",
139
+ " const u = clamp01((t - rotateLeftEnd) / Math.max(1e-6, 1 - rotateLeftEnd));",
140
+ " moveRoot.position.x = lerp(xMax, xMin, u);",
141
+ " turnRoot.rotation.y = faceLeft;",
142
+ " }",
143
+ " }",
144
+ " camera.lookAt(0, 0.9, 0);",
145
+ " renderer.render(scene, camera);",
146
+ "};"
147
+ ],
148
+ "animation": {
149
+ "fps": 30
150
+ }
151
+ }
152
+ }
153
+ ]
154
+ }
@@ -0,0 +1,39 @@
1
+ {
2
+ "$mulmocast": { "version": "1.1" },
3
+ "title": "Veo 3.1 Lite Test",
4
+ "lang": "en",
5
+ "canvasSize": { "width": 1280, "height": 720 },
6
+ "beats": [
7
+ {
8
+ "id": "google_genai_lite",
9
+ "text": "Google GenAI veo-3.1-lite-generate-preview text-to-video",
10
+ "duration": 4,
11
+ "moviePrompt": "A calm ocean wave rolling onto a sandy beach at golden hour",
12
+ "movieParams": {
13
+ "provider": "google",
14
+ "model": "veo-3.1-lite-generate-preview"
15
+ }
16
+ },
17
+ {
18
+ "id": "replicate_lite",
19
+ "text": "Replicate google/veo-3.1-lite text-to-video",
20
+ "duration": 4,
21
+ "moviePrompt": "A calm ocean wave rolling onto a sandy beach at golden hour",
22
+ "movieParams": {
23
+ "provider": "replicate",
24
+ "model": "google/veo-3.1-lite"
25
+ }
26
+ },
27
+ {
28
+ "id": "replicate_lite_i2v",
29
+ "text": "Replicate google/veo-3.1-lite image-to-video",
30
+ "duration": 4,
31
+ "imagePrompt": "A beautiful ocean sunset with golden reflections on the water",
32
+ "moviePrompt": "The camera slowly pans across the ocean as waves gently roll in",
33
+ "movieParams": {
34
+ "provider": "replicate",
35
+ "model": "google/veo-3.1-lite"
36
+ }
37
+ }
38
+ ]
39
+ }