create-aura3d 1.0.10 → 1.1.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.
Files changed (53) hide show
  1. package/package.json +1 -1
  2. package/templates/cartoon-channel/README.md +38 -0
  3. package/templates/cartoon-channel/aura.assets.json +1364 -0
  4. package/templates/cartoon-channel/package.json +2 -1
  5. package/templates/cartoon-channel/public/aura-assets/luma.047f5e5f.glb +0 -0
  6. package/templates/cartoon-channel/public/aura-assets/luma.thumb.svg +1 -0
  7. package/templates/cartoon-channel/public/aura-assets/miko.047f5e5f.glb +0 -0
  8. package/templates/cartoon-channel/public/aura-assets/miko.thumb.svg +1 -0
  9. package/templates/cartoon-channel/public/aura-assets/moon-garden-feature-frame.png +0 -0
  10. package/templates/cartoon-channel/src/aura-assets.ts +5 -11
  11. package/templates/cartoon-channel/src/concept-episode-2-5d.css +159 -0
  12. package/templates/cartoon-channel/src/concept-episode-2-5d.ts +155 -0
  13. package/templates/cartoon-channel/src/image-puppet-episode.css +290 -0
  14. package/templates/cartoon-channel/src/image-puppet-episode.ts +139 -0
  15. package/templates/cartoon-channel/src/main.ts +89 -71
  16. package/templates/cartoon-channel/src/puppet-episode-2d.css +526 -0
  17. package/templates/cartoon-channel/src/puppet-episode-2d.ts +189 -0
  18. package/templates/cartoon-channel/src/render-plan.ts +1 -1
  19. package/templates/cartoon-channel/src/sample-episode-visual.css +457 -0
  20. package/templates/cartoon-channel/src/sample-episode-visual.ts +185 -0
  21. package/templates/cartoon-channel/test-results/.last-run.json +4 -0
  22. package/templates/cartoon-channel/tests/concept-2-5d-animation.spec.ts +18 -0
  23. package/templates/cartoon-channel/tests/concept-2-5d.spec.ts +30 -0
  24. package/templates/cartoon-channel/tests/image-puppet-animation.spec.ts +18 -0
  25. package/templates/cartoon-channel/tests/image-puppet.spec.ts +29 -0
  26. package/templates/cartoon-channel/tests/puppet-2d-animation.spec.ts +18 -0
  27. package/templates/cartoon-channel/tests/puppet-2d.spec.ts +26 -0
  28. package/templates/cartoon-channel/tests/reports/prompt-animation/cartoon-sample-episode.png +0 -0
  29. package/templates/cartoon-channel/tests/sample-episode-visual.spec.ts +45 -0
  30. package/templates/cartoon-channel/tests/storyboard-playback.spec.ts +1 -1
  31. package/templates/cartoon-studio/README.md +36 -0
  32. package/templates/cartoon-studio/aura.assets.json +157 -0
  33. package/templates/cartoon-studio/package.json +12 -1
  34. package/templates/cartoon-studio/public/aura-assets/luma.047f5e5f.glb +0 -0
  35. package/templates/cartoon-studio/public/aura-assets/luma.humanoid-fixture.glb +0 -0
  36. package/templates/cartoon-studio/public/aura-assets/luma.thumb.svg +1 -0
  37. package/templates/cartoon-studio/public/aura-assets/miko.047f5e5f.glb +0 -0
  38. package/templates/cartoon-studio/public/aura-assets/miko.thumb.svg +1 -0
  39. package/templates/cartoon-studio/public/aura-assets/moonGarden.gltf +183 -0
  40. package/templates/cartoon-studio/scripts/episode.ts +276 -0
  41. package/templates/cartoon-studio/src/aura-assets.ts +55 -8
  42. package/templates/cartoon-studio/src/characters.ts +38 -1
  43. package/templates/cartoon-studio/src/episode-renderer.ts +605 -0
  44. package/templates/cartoon-studio/src/episode.ts +12 -6
  45. package/templates/cartoon-studio/src/main.ts +109 -41
  46. package/templates/cartoon-studio/src/render-plan.ts +162 -0
  47. package/templates/cartoon-studio/src/review.ts +99 -0
  48. package/templates/cartoon-studio/test-results/.last-run.json +4 -0
  49. package/templates/cartoon-studio/tests/episode-render.spec.ts +44 -0
  50. package/templates/cartoon-studio/tests/motion-quality.spec.ts +45 -0
  51. package/templates/cartoon-studio/tests/storyboard-playback.spec.ts +18 -10
  52. package/templates/cartoon-studio/tests/visual-review.spec.ts +21 -0
  53. package/templates/product-viewer/tests/screenshot.spec.ts +8 -16
@@ -0,0 +1,290 @@
1
+ body[data-cartoon-view="image-puppet"] {
2
+ background:
3
+ radial-gradient(circle at 50% 20%, rgba(115, 225, 255, 0.12), transparent 34%),
4
+ linear-gradient(180deg, #06101d 0%, #102820 58%, #07100f 100%);
5
+ }
6
+
7
+ body[data-cartoon-view="image-puppet"] #caption-overlay,
8
+ body[data-cartoon-view="image-puppet"] .aura-diagnostics-overlay {
9
+ display: none !important;
10
+ }
11
+
12
+ body[data-cartoon-view="image-puppet"] #app {
13
+ opacity: 0;
14
+ }
15
+
16
+ .image-puppet {
17
+ --source-frame: url("/aura-assets/moon-garden-feature-frame.png");
18
+ position: fixed;
19
+ inset: 0;
20
+ display: grid;
21
+ place-items: center;
22
+ overflow: hidden;
23
+ padding: 28px;
24
+ background:
25
+ radial-gradient(circle at 50% 32%, rgba(255, 226, 150, 0.11), transparent 28%),
26
+ linear-gradient(180deg, rgba(7, 19, 28, 0.64), rgba(1, 8, 10, 0.9));
27
+ }
28
+
29
+ .image-puppet__frame {
30
+ position: relative;
31
+ width: min(1320px, calc(100vw - 56px));
32
+ aspect-ratio: 1672 / 941;
33
+ overflow: hidden;
34
+ border: 1px solid rgba(255, 232, 170, 0.28);
35
+ border-radius: 22px;
36
+ background: #06100f;
37
+ box-shadow:
38
+ 0 36px 94px rgba(0, 0, 0, 0.58),
39
+ 0 0 90px rgba(64, 255, 191, 0.12);
40
+ isolation: isolate;
41
+ }
42
+
43
+ .image-puppet__base,
44
+ .image-puppet__cutout {
45
+ position: absolute;
46
+ inset: 0;
47
+ background-image: var(--source-frame);
48
+ background-repeat: no-repeat;
49
+ background-position: 50% 50%;
50
+ background-size: 100% 100%;
51
+ }
52
+
53
+ .image-puppet__base {
54
+ z-index: 1;
55
+ filter: saturate(0.96) brightness(0.78);
56
+ transform: scale(1.01);
57
+ }
58
+
59
+ .image-puppet__matte {
60
+ position: absolute;
61
+ z-index: 2;
62
+ border-radius: 999px;
63
+ background:
64
+ radial-gradient(ellipse at 50% 50%, rgba(8, 30, 36, 0.78), rgba(8, 30, 36, 0.4) 52%, transparent 72%);
65
+ filter: blur(10px);
66
+ }
67
+
68
+ .image-puppet__matte--blue {
69
+ left: 28%;
70
+ top: 40%;
71
+ width: 22%;
72
+ height: 42%;
73
+ }
74
+
75
+ .image-puppet__matte--yellow {
76
+ left: 53%;
77
+ top: 33%;
78
+ width: 22%;
79
+ height: 47%;
80
+ }
81
+
82
+ .image-puppet__matte--cart {
83
+ left: 46%;
84
+ top: 62%;
85
+ width: 17%;
86
+ height: 18%;
87
+ }
88
+
89
+ .image-puppet__cutout {
90
+ z-index: 5;
91
+ will-change: transform;
92
+ filter:
93
+ saturate(1.2)
94
+ brightness(1.1)
95
+ drop-shadow(0 20px 22px rgba(0, 0, 0, 0.34));
96
+ }
97
+
98
+ .image-puppet__cutout--moon {
99
+ z-index: 3;
100
+ transform-origin: 58% 25%;
101
+ animation: sourceMoonPulse 2.4s ease-in-out infinite;
102
+ -webkit-mask-image: radial-gradient(ellipse 17% 20% at 58% 25%, black 0 58%, transparent 72%);
103
+ mask-image: radial-gradient(ellipse 17% 20% at 58% 25%, black 0 58%, transparent 72%);
104
+ filter: saturate(1.15) brightness(1.12) drop-shadow(0 0 30px rgba(115, 230, 255, 0.42));
105
+ }
106
+
107
+ .image-puppet__cutout--blue-body {
108
+ transform-origin: 35% 70%;
109
+ animation: sourceBlueBody 1.25s ease-in-out infinite;
110
+ -webkit-mask-image: radial-gradient(ellipse 9% 18% at 35% 67%, black 0 57%, transparent 72%);
111
+ mask-image: radial-gradient(ellipse 9% 18% at 35% 67%, black 0 57%, transparent 72%);
112
+ }
113
+
114
+ .image-puppet__cutout--blue-head {
115
+ z-index: 6;
116
+ transform-origin: 36.5% 54%;
117
+ animation: sourceBlueHead 1.25s ease-in-out infinite;
118
+ -webkit-mask-image: radial-gradient(ellipse 8% 10% at 36.5% 54%, black 0 56%, transparent 72%);
119
+ mask-image: radial-gradient(ellipse 8% 10% at 36.5% 54%, black 0 56%, transparent 72%);
120
+ }
121
+
122
+ .image-puppet__cutout--blue-broom {
123
+ z-index: 7;
124
+ transform-origin: 42% 70%;
125
+ animation: sourceBroomSweep 0.88s ease-in-out infinite;
126
+ -webkit-mask-image:
127
+ linear-gradient(118deg, transparent 0 39%, black 41% 45%, transparent 48% 100%),
128
+ radial-gradient(ellipse 5% 5% at 42% 83%, black 0 52%, transparent 70%);
129
+ mask-image:
130
+ linear-gradient(118deg, transparent 0 39%, black 41% 45%, transparent 48% 100%),
131
+ radial-gradient(ellipse 5% 5% at 42% 83%, black 0 52%, transparent 70%);
132
+ }
133
+
134
+ .image-puppet__cutout--cart {
135
+ z-index: 5;
136
+ transform-origin: 53% 72%;
137
+ animation: sourceCartRoll 1.15s ease-in-out infinite;
138
+ -webkit-mask-image: radial-gradient(ellipse 10% 8% at 52.5% 72%, black 0 60%, transparent 75%);
139
+ mask-image: radial-gradient(ellipse 10% 8% at 52.5% 72%, black 0 60%, transparent 75%);
140
+ }
141
+
142
+ .image-puppet__cutout--yellow-body {
143
+ transform-origin: 61% 67%;
144
+ animation: sourceYellowBody 1.6s ease-in-out infinite;
145
+ -webkit-mask-image: radial-gradient(ellipse 9% 20% at 61% 64%, black 0 60%, transparent 74%);
146
+ mask-image: radial-gradient(ellipse 9% 20% at 61% 64%, black 0 60%, transparent 74%);
147
+ }
148
+
149
+ .image-puppet__cutout--yellow-head {
150
+ z-index: 6;
151
+ transform-origin: 61% 45%;
152
+ animation: sourceYellowHead 1.6s ease-in-out infinite;
153
+ -webkit-mask-image: radial-gradient(ellipse 8% 10% at 61% 45%, black 0 57%, transparent 72%);
154
+ mask-image: radial-gradient(ellipse 8% 10% at 61% 45%, black 0 57%, transparent 72%);
155
+ }
156
+
157
+ .image-puppet__cutout--yellow-tool {
158
+ z-index: 7;
159
+ transform-origin: 57% 68%;
160
+ animation: sourceYellowTool 1.15s ease-in-out infinite;
161
+ -webkit-mask-image:
162
+ linear-gradient(73deg, transparent 0 43%, black 45% 48%, transparent 51% 100%),
163
+ radial-gradient(ellipse 8% 7% at 58% 67%, black 0 54%, transparent 72%);
164
+ mask-image:
165
+ linear-gradient(73deg, transparent 0 43%, black 45% 48%, transparent 51% 100%),
166
+ radial-gradient(ellipse 8% 7% at 58% 67%, black 0 54%, transparent 72%);
167
+ }
168
+
169
+ .image-puppet__cutout--foreground {
170
+ z-index: 9;
171
+ transform-origin: 50% 91%;
172
+ animation: sourceForegroundGlow 2s ease-in-out infinite alternate;
173
+ -webkit-mask-image: linear-gradient(180deg, transparent 0 70%, rgba(0, 0, 0, 0.42) 78%, black 100%);
174
+ mask-image: linear-gradient(180deg, transparent 0 70%, rgba(0, 0, 0, 0.42) 78%, black 100%);
175
+ filter: saturate(1.18) brightness(1.08) drop-shadow(0 -12px 24px rgba(115, 230, 255, 0.18));
176
+ }
177
+
178
+ .image-puppet__hud,
179
+ .image-puppet__caption {
180
+ position: absolute;
181
+ z-index: 12;
182
+ left: 28px;
183
+ right: 28px;
184
+ display: flex;
185
+ align-items: center;
186
+ gap: 14px;
187
+ color: #f8fff2;
188
+ text-shadow: 0 2px 14px rgba(0, 0, 0, 0.72);
189
+ }
190
+
191
+ .image-puppet__hud {
192
+ top: 24px;
193
+ justify-content: space-between;
194
+ font: 700 13px/1.2 ui-sans-serif, system-ui, sans-serif;
195
+ letter-spacing: 0.08em;
196
+ text-transform: uppercase;
197
+ }
198
+
199
+ .image-puppet__hud span,
200
+ .image-puppet__hud strong {
201
+ padding: 8px 12px;
202
+ border: 1px solid rgba(248, 255, 242, 0.18);
203
+ border-radius: 999px;
204
+ background: rgba(1, 10, 10, 0.58);
205
+ backdrop-filter: blur(8px);
206
+ }
207
+
208
+ .image-puppet__caption {
209
+ bottom: 42px;
210
+ justify-content: center;
211
+ text-align: center;
212
+ font: 800 clamp(18px, 2.2vw, 30px) / 1.24 ui-rounded, "Trebuchet MS", system-ui, sans-serif;
213
+ }
214
+
215
+ .image-puppet__caption::before {
216
+ content: "";
217
+ position: absolute;
218
+ inset: -14px auto auto 50%;
219
+ width: min(880px, calc(100vw - 120px));
220
+ height: calc(100% + 28px);
221
+ transform: translateX(-50%);
222
+ border-radius: 18px;
223
+ background: linear-gradient(180deg, rgba(1, 10, 10, 0.1), rgba(1, 10, 10, 0.68));
224
+ z-index: -1;
225
+ }
226
+
227
+ .image-puppet__timeline {
228
+ position: absolute;
229
+ z-index: 13;
230
+ left: 28px;
231
+ right: 28px;
232
+ bottom: 20px;
233
+ height: 4px;
234
+ overflow: hidden;
235
+ border-radius: 999px;
236
+ background: rgba(248, 255, 242, 0.16);
237
+ }
238
+
239
+ .image-puppet__timeline-fill {
240
+ display: block;
241
+ width: var(--progress, 0%);
242
+ height: 100%;
243
+ border-radius: inherit;
244
+ background: linear-gradient(90deg, #ffdd87, #65ffc6);
245
+ }
246
+
247
+ @keyframes sourceMoonPulse {
248
+ 0%, 100% { transform: scale(0.985); opacity: 0.52; }
249
+ 50% { transform: scale(1.035); opacity: 0.88; }
250
+ }
251
+
252
+ @keyframes sourceBlueBody {
253
+ 0%, 100% { transform: translate(-2px, 0) rotate(-1.2deg); }
254
+ 50% { transform: translate(7px, 8px) rotate(2.6deg); }
255
+ }
256
+
257
+ @keyframes sourceBlueHead {
258
+ 0%, 100% { transform: translate(0, 0) rotate(-2deg); }
259
+ 50% { transform: translate(5px, 8px) rotate(4deg); }
260
+ }
261
+
262
+ @keyframes sourceBroomSweep {
263
+ 0%, 100% { transform: translate(-8px, 0) rotate(-5deg); }
264
+ 50% { transform: translate(15px, 9px) rotate(8deg); }
265
+ }
266
+
267
+ @keyframes sourceCartRoll {
268
+ 0%, 100% { transform: translateX(0) rotate(0deg); }
269
+ 50% { transform: translateX(-10px) rotate(-1.6deg); }
270
+ }
271
+
272
+ @keyframes sourceYellowBody {
273
+ 0%, 100% { transform: translate(0, 0) rotate(1deg); }
274
+ 50% { transform: translate(-9px, 3px) rotate(-1.8deg); }
275
+ }
276
+
277
+ @keyframes sourceYellowHead {
278
+ 0%, 100% { transform: translate(0, 0) rotate(1.5deg); }
279
+ 50% { transform: translate(-7px, 4px) rotate(-3deg); }
280
+ }
281
+
282
+ @keyframes sourceYellowTool {
283
+ 0%, 100% { transform: translate(0, 0) rotate(0deg); }
284
+ 50% { transform: translate(-13px, 9px) rotate(-5deg); }
285
+ }
286
+
287
+ @keyframes sourceForegroundGlow {
288
+ from { opacity: 0.62; transform: scale(1); }
289
+ to { opacity: 0.86; transform: scale(1.012); }
290
+ }
@@ -0,0 +1,139 @@
1
+ import { episode, episodeSampleDescription } from "./episode";
2
+ import type { CartoonRouteSampleForVisual } from "./sample-episode-visual";
3
+ import "./image-puppet-episode.css";
4
+
5
+ export interface ImagePuppetEpisodeOptions {
6
+ readonly sampleAt: (time: number) => CartoonRouteSampleForVisual;
7
+ readonly duration: number;
8
+ readonly fixedTime?: number;
9
+ }
10
+
11
+ export interface ImagePuppetEpisodeProof {
12
+ readonly contractId: string;
13
+ readonly episodeId: string;
14
+ readonly title: string;
15
+ readonly route: string;
16
+ readonly mode: "image-derived-puppet-cutouts";
17
+ readonly sourceImage: string;
18
+ readonly sourcePixelsAnimated: true;
19
+ readonly notTrue3D: true;
20
+ readonly screenshotTarget: "#image-puppet-episode";
21
+ readonly sample: CartoonRouteSampleForVisual;
22
+ readonly movingCutouts: readonly string[];
23
+ readonly limitations: readonly string[];
24
+ }
25
+
26
+ const sourceImage = "/aura-assets/moon-garden-feature-frame.png";
27
+ const movingCutouts = [
28
+ "blue robot source-pixel head/body cutout",
29
+ "blue robot source-pixel broom area",
30
+ "yellow robot source-pixel head/body cutout",
31
+ "yellow robot source-pixel arm/tool area",
32
+ "wheelbarrow source-pixel cutout",
33
+ "foreground flower source-pixel layer",
34
+ "moon source-pixel pulse layer"
35
+ ] as const;
36
+
37
+ export function installImagePuppetEpisode(options: ImagePuppetEpisodeOptions) {
38
+ document.body.dataset.cartoonView = "image-puppet";
39
+ const root = document.createElement("section");
40
+ root.id = "image-puppet-episode";
41
+ root.className = "image-puppet";
42
+ root.setAttribute("aria-label", "Image-derived puppet animation using cutouts from the moon garden source PNG");
43
+ root.innerHTML = `
44
+ <div class="image-puppet__frame">
45
+ <div class="image-puppet__base" aria-hidden="true"></div>
46
+ <div class="image-puppet__matte image-puppet__matte--blue" aria-hidden="true"></div>
47
+ <div class="image-puppet__matte image-puppet__matte--yellow" aria-hidden="true"></div>
48
+ <div class="image-puppet__matte image-puppet__matte--cart" aria-hidden="true"></div>
49
+
50
+ <div class="image-puppet__cutout image-puppet__cutout--moon" data-source-cutout="moon" aria-hidden="true"></div>
51
+ <div class="image-puppet__cutout image-puppet__cutout--blue-body" data-source-cutout="blue-body" aria-hidden="true"></div>
52
+ <div class="image-puppet__cutout image-puppet__cutout--blue-head" data-source-cutout="blue-head" aria-hidden="true"></div>
53
+ <div class="image-puppet__cutout image-puppet__cutout--blue-broom" data-source-cutout="blue-broom" aria-hidden="true"></div>
54
+ <div class="image-puppet__cutout image-puppet__cutout--cart" data-source-cutout="cart" aria-hidden="true"></div>
55
+ <div class="image-puppet__cutout image-puppet__cutout--yellow-body" data-source-cutout="yellow-body" aria-hidden="true"></div>
56
+ <div class="image-puppet__cutout image-puppet__cutout--yellow-head" data-source-cutout="yellow-head" aria-hidden="true"></div>
57
+ <div class="image-puppet__cutout image-puppet__cutout--yellow-tool" data-source-cutout="yellow-tool" aria-hidden="true"></div>
58
+ <div class="image-puppet__cutout image-puppet__cutout--foreground" data-source-cutout="foreground" aria-hidden="true"></div>
59
+
60
+ <div class="image-puppet__hud">
61
+ <span>Image-derived cutouts</span>
62
+ <strong data-image-puppet-shot-label>Shot 1 / 3</strong>
63
+ </div>
64
+ <div class="image-puppet__caption" data-image-puppet-caption></div>
65
+ <div class="image-puppet__timeline" aria-hidden="true">
66
+ <span class="image-puppet__timeline-fill" data-image-puppet-progress></span>
67
+ </div>
68
+ </div>
69
+ `;
70
+ document.body.appendChild(root);
71
+
72
+ const caption = root.querySelector<HTMLElement>("[data-image-puppet-caption]");
73
+ const shotLabel = root.querySelector<HTMLElement>("[data-image-puppet-shot-label]");
74
+ const progress = root.querySelector<HTMLElement>("[data-image-puppet-progress]");
75
+ const query = new URLSearchParams(window.location.search);
76
+ const queryTime = query.get("sampleTime");
77
+ const fixedTime = options.fixedTime ?? (queryTime ? Number(queryTime) : undefined);
78
+ const start = performance.now();
79
+
80
+ function render(time: number) {
81
+ const sample = options.sampleAt(clampTime(time, options.duration));
82
+ const shotIndex = Math.max(0, episode.shotTimeline.shots.findIndex((shot) => shot.shotId === sample.shotId));
83
+ const progressPercent = Math.max(0, Math.min(100, (sample.time / options.duration) * 100));
84
+
85
+ root.dataset.shotId = sample.shotId ?? "";
86
+ root.dataset.captionId = sample.captionId ?? "";
87
+ root.dataset.sourceImage = sourceImage;
88
+
89
+ if (caption) {
90
+ caption.textContent = sample.captionText ?? "The source image is duplicated into moving cutout layers.";
91
+ }
92
+ if (shotLabel) {
93
+ shotLabel.textContent = `Shot ${shotIndex + 1} / ${episode.shotTimeline.shots.length}`;
94
+ }
95
+ if (progress) {
96
+ progress.style.setProperty("--progress", `${progressPercent}%`);
97
+ }
98
+
99
+ window.__AURA3D_CARTOON_IMAGE_PUPPET_PROOF__ = {
100
+ contractId: episodeSampleDescription.contractId,
101
+ episodeId: episode.episodePlan.episodeId,
102
+ title: episode.episodePlan.title,
103
+ route: "/?view=image-puppet",
104
+ mode: "image-derived-puppet-cutouts",
105
+ sourceImage,
106
+ sourcePixelsAnimated: true,
107
+ notTrue3D: true,
108
+ screenshotTarget: "#image-puppet-episode",
109
+ sample,
110
+ movingCutouts,
111
+ limitations: [
112
+ "The PNG is flattened, so these are approximate masked source-pixel cutouts, not clean production layers.",
113
+ "The original still remains visible under the moving cutouts unless a real background plate or inpainted clean plate exists.",
114
+ "This is image-derived 2D animation, not 3D reconstruction or rigged GLB character animation."
115
+ ]
116
+ };
117
+ }
118
+
119
+ render(Number.isFinite(fixedTime) ? Number(fixedTime) : 0);
120
+ const tick = () => {
121
+ if (!Number.isFinite(fixedTime)) {
122
+ render(((performance.now() - start) / 1000) % options.duration);
123
+ }
124
+ requestAnimationFrame(tick);
125
+ };
126
+ tick();
127
+ return root;
128
+ }
129
+
130
+ declare global {
131
+ interface Window {
132
+ __AURA3D_CARTOON_IMAGE_PUPPET_PROOF__?: ImagePuppetEpisodeProof;
133
+ }
134
+ }
135
+
136
+ function clampTime(time: number, duration: number) {
137
+ if (!Number.isFinite(time)) return 0;
138
+ return Math.max(0, Math.min(duration - 1 / 30, time));
139
+ }
@@ -16,6 +16,7 @@ import {
16
16
  scene,
17
17
  type AuraAssetRef
18
18
  } from "@aura3d/engine";
19
+ import "./sample-episode-visual.css";
19
20
  import { assets } from "./aura-assets";
20
21
  import {
21
22
  episode,
@@ -45,6 +46,7 @@ import {
45
46
  visemeFrameSyncSourceProof,
46
47
  visemeTrack
47
48
  } from "./render-plan";
49
+ import { installSampleEpisodeVisual } from "./sample-episode-visual";
48
50
 
49
51
  declare global {
50
52
  interface Window {
@@ -64,8 +66,12 @@ declare global {
64
66
  readonly bridgeIssues: readonly unknown[];
65
67
  readonly promptAnimationEvidence: unknown;
66
68
  readonly publishReadiness: unknown;
69
+ readonly releaseFacingViews: readonly string[];
70
+ readonly rejectedViews: readonly string[];
71
+ readonly rejectedViewReason: string;
67
72
  readonly sourceProofs: unknown;
68
73
  readonly sampleRenderSourceWorkflow: unknown;
74
+ diagnostics(): unknown;
69
75
  sampleAt(time: number): unknown;
70
76
  };
71
77
  }
@@ -118,14 +124,22 @@ captionOverlay.dataset.captionId = playbackProbeSamples[0]?.captionId ?? firstCa
118
124
  document.body.dataset.cartoonShotCount = String(episode.shotTimeline.shots.length);
119
125
  document.body.dataset.cartoonCaptionCount = String(episode.captionTrack.cues.length);
120
126
  document.body.dataset.cartoonStoryBibleProps = String(storyBible.props.length);
127
+ document.body.dataset.cartoonReleaseFacingView = "sample-episode-visual";
128
+ document.body.dataset.cartoonRejectedViews = "concept-2-5d,puppet-2d,image-puppet";
129
+ installSampleEpisodeVisual({
130
+ sampleAt: sampleCartoonRouteAt,
131
+ duration: episode.episodePlan.runtime.duration,
132
+ usesTypedAssets: missingCartoonCharacterAssets.length === 0
133
+ });
121
134
 
122
135
  const app = createAuraApp("#app", {
123
136
  scene: scene()
124
137
  .background("#081b2a")
125
- .add(createCharacterBody("miko", "Miko", "miko", [-0.8, 0.75, 0], [0.32, 0.42, 0.32], 0.72, "#7de2ff"))
126
- .add(createMouthCard("miko:mouth", "Miko", [-0.8, 0.8, 0.32], "#f8fff2"))
127
- .add(createCharacterBody("luma", "Luma", "luma", [0.8, 0.72, 0], [0.3, 0.38, 0.3], 0.68, "#ffe18e"))
128
- .add(createMouthCard("luma:mouth", "Luma", [0.8, 0.77, 0.32], "#40ffbf"))
138
+ .addMany(createAuraRenderedCartoonScene())
139
+ .add(createCharacterBody("miko", "Miko", "miko", [-0.72, 0.08, -0.34], [0.32, 0.42, 0.32], 0.9, "#7de2ff"))
140
+ .add(createMouthCard("miko:mouth", "Miko", [-3.2, -3.2, -3.2], "#f8fff2"))
141
+ .add(createCharacterBody("luma", "Luma", "luma", [0.72, 0.08, -0.34], [0.3, 0.38, 0.3], 0.9, "#ffe18e"))
142
+ .add(createMouthCard("luma:mouth", "Luma", [3.2, -3.2, -3.2], "#40ffbf"))
129
143
  .add(
130
144
  primitives
131
145
  .box({ name: "moon garden path", material: material.pbr({ color: "#182d3f", roughness: 0.78 }) })
@@ -134,7 +148,7 @@ const app = createAuraApp("#app", {
134
148
  )
135
149
  .add(
136
150
  primitives
137
- .sphere({ name: "glow stones primitive fallback", material: material.emissive({ color: "#ffe18e", intensity: 0.42 }) })
151
+ .sphere({ name: "glow stones primitive fallback", material: material.emissive({ color: "#ffe18e" }) })
138
152
  .position(0.18, 0.16, 0.28)
139
153
  .scale([0.16, 0.08, 0.16])
140
154
  )
@@ -144,73 +158,11 @@ const app = createAuraApp("#app", {
144
158
  .position(-0.38, 0.2, 0.18)
145
159
  .scale([0.12, 0.24, 0.04])
146
160
  )
147
- .add(labels.hud({ text: firstCaption?.text ?? firstStoryboardShot?.storyBeat ?? "Aura3D cartoon channel", position: "bottom-center" }))
161
+ .add(labels.hud(firstCaption?.text ?? firstStoryboardShot?.storyBeat ?? "Aura3D cartoon channel"))
148
162
  .add(lights.studio({ intensity: 1.2 }))
149
163
  .add(effects.bloom({ intensity: 0.18, color: "#7de2ff" }))
150
- .camera(camera.perspective({ position: [0, 1.4, 4.2], target: [0, 0.7, 0], fov: 42 })),
151
- diagnostics: true,
152
- metadata: {
153
- contractId: episodeContractId,
154
- template: "cartoon-channel",
155
- runtimePlayback: "app.onFrame updates runtime nodes; createAuraApp is called once",
156
- renderQueueItems: renderPlan.items.length,
157
- renderPackageId: renderOutputPackage.packageId,
158
- storyBibleId: storyBible.episodeId,
159
- storyBibleProps: storyBible.props.map((prop) => prop.id),
160
- styleGuide: storyBible.styleGuide.visualStyle,
161
- shotListCount: storyBible.shotList.length,
162
- playbackProbeSamples: playbackProbeSamples.map((sample) => ({
163
- time: sample.time,
164
- frame: sample.frame,
165
- shotId: sample.shotId,
166
- captionId: sample.captionId,
167
- nodeUpdateCount: sample.nodeUpdates.length
168
- })),
169
- thumbnailCaptureSceneStateId: thumbnailCapture.sourceSceneStateId,
170
- thumbnailCaptureTime: thumbnailCapture.time,
171
- thumbnailCaptionId: thumbnailCaption?.captionId,
172
- deterministicCaptureItems: renderPlan.items.filter((item) => item.sourceSceneState).length,
173
- deterministicScreenshotFixtures,
174
- typedAssets: typedCartoonAssetSummary,
175
- requiredTypedAssets: requiredCartoonCharacterAssets,
176
- missingTypedAssets: missingCartoonCharacterAssets,
177
- assetCommands: publicCartoonAssetInstructions,
178
- bridgeIssueCount: bridgeIssues.length,
179
- bridgeSampleAtThumbnail: {
180
- time: bridgeSampleAtThumbnail.time,
181
- frame: bridgeSampleAtThumbnail.frame,
182
- shotId: bridgeSampleAtThumbnail.shot?.shotId,
183
- dialogueLineId: bridgeSampleAtThumbnail.dialogue?.lineId
184
- },
185
- promptAnimationEvidence: {
186
- publishReady: promptAnimationEvidence.publishReady,
187
- assetStatus: promptAnimationEvidence.assetStatus,
188
- captions: promptAnimationEvidence.captions,
189
- visemes: promptAnimationEvidence.visemes,
190
- audio: promptAnimationEvidence.audio,
191
- deterministicCapture: promptAnimationEvidence.deterministicCapture,
192
- routeHealth: promptAnimationEvidence.routeHealth
193
- },
194
- publishReadiness: {
195
- ready: publishReadiness.ready,
196
- issueCount: publishReadiness.issues.length
197
- },
198
- captionTimingProof: {
199
- status: captionTimingProof.status,
200
- maxDriftFrames: captionTimingProof.maxDriftFrames,
201
- coveredLineIds: captionTimingProof.coveredLineIds
202
- },
203
- captionFrameSyncSourceProof,
204
- visemeFrameSyncSourceProof,
205
- sampleRenderSourceWorkflow,
206
- accessibilityProof: {
207
- captions: accessibilityProofMetadata?.captions.status,
208
- reducedMotion: accessibilityProofMetadata?.reducedMotion.status,
209
- highContrast: accessibilityProofMetadata?.highContrast.status
210
- },
211
- primitiveMouthRuntimeExample,
212
- glbVisemeRuntimeExample
213
- }
164
+ .camera(camera.perspective({ position: [0, 1.18, 4.4], target: [0, 0.82, -0.45], fov: 38 })),
165
+ diagnostics: true
214
166
  });
215
167
 
216
168
  window.__AURA3D_CARTOON_TEMPLATE__ = {
@@ -229,12 +181,19 @@ window.__AURA3D_CARTOON_TEMPLATE__ = {
229
181
  bridgeIssues,
230
182
  promptAnimationEvidence,
231
183
  publishReadiness,
184
+ releaseFacingViews: ["sample-episode-visual"],
185
+ rejectedViews: ["concept-2-5d", "puppet-2d", "image-puppet"],
186
+ rejectedViewReason:
187
+ "Rejected 1.0.10 puppet/parallax experiments are quarantined from the release-facing cartoon-channel route because still-image parallax, flat cutout motion, and notTrue3D proof cannot satisfy Aura3D 1.1 cartoon animation readiness.",
232
188
  sourceProofs: {
233
189
  captionFrameSyncSourceProof,
234
190
  visemeFrameSyncSourceProof,
235
191
  phonemeVisemeDubSyncSourceProof
236
192
  },
237
193
  sampleRenderSourceWorkflow,
194
+ diagnostics() {
195
+ return app.diagnostics();
196
+ },
238
197
  sampleAt(time: number) {
239
198
  return sampleCartoonRouteAt(time);
240
199
  }
@@ -267,6 +226,7 @@ function createCharacterBody(
267
226
  return model(asset, { name: `${name} typed character asset` })
268
227
  .position(position[0], position[1], position[2])
269
228
  .scale(modelScale)
229
+ .material(material.pbr({ color, roughness: 0.38, metallic: 0.02 }))
270
230
  .runtime(runtime);
271
231
  }
272
232
  return primitives
@@ -283,13 +243,71 @@ function createMouthCard(id: "miko:mouth" | "luma:mouth", name: string, position
283
243
  return primitives
284
244
  .box({
285
245
  name: `${name} primitive mouth card`,
286
- material: material.emissive({ color, intensity: 0.32 })
246
+ material: material.emissive({ color })
287
247
  })
288
248
  .position(position[0], position[1], position[2])
289
249
  .scale([0.22, 0.035, 0.025])
290
250
  .runtime(game.runtimeNode(id, { tags: ["mouth", "primitive", name.toLowerCase()] }));
291
251
  }
292
252
 
253
+ function createAuraRenderedCartoonScene() {
254
+ const skyMat = material.emissive({ color: "#0a2941", emissive: "#0f4765", emissiveIntensity: 0.18 });
255
+ const gardenMat = material.pbr({ color: "#17493e", roughness: 0.64, metallic: 0.02 });
256
+ const pathMat = material.clearcoat({ color: "#526b62", roughness: 0.2, clearcoat: 0.4 });
257
+ const shadowMat = material.pbr({ color: "#020813", roughness: 0.96, opacity: 0.44 });
258
+ const moonMat = material.emissive({ color: "#b7f4ff", emissive: "#7de2ff", emissiveIntensity: 0.82, opacity: 0.72 });
259
+ const bloomCyan = material.emissive({ color: "#7de2ff", emissive: "#7de2ff", emissiveIntensity: 1.45 });
260
+ const bloomGold = material.emissive({ color: "#ffe18e", emissive: "#ffe18e", emissiveIntensity: 1.35 });
261
+ return [
262
+ primitives.plane({ name: "Aura3D moon garden sky plate", material: skyMat }).position(0, 1.35, -2.15).rotate(-0.04, 0, 0).scale([5.8, 1, 2.5]),
263
+ primitives.sphere({ name: "Aura3D glowing moon portal core", material: moonMat }).position(0, 1.35, -1.95).scale([0.62, 0.62, 0.04]),
264
+ primitives.torus({ name: "Aura3D moon portal rim", material: bloomCyan }).position(0, 1.35, -1.94).scale([0.78, 0.78, 0.026]),
265
+ ...createCartoonCitySilhouette(),
266
+ primitives.plane({ name: "Aura3D moon garden floor", material: gardenMat }).position(0, -0.02, -0.45).scale([4.8, 1, 2.6]),
267
+ primitives.cylinder({ name: "Aura3D curved garden mound", material: gardenMat }).position(0, 0.03, -0.64).scale([1.72, 0.08, 0.74]),
268
+ primitives.box({ name: "Aura3D warm stone path", material: pathMat }).position(0, 0.06, 0.1).scale([1.58, 0.04, 0.28]),
269
+ primitives.cylinder({ name: "Aura3D blue robot contact shadow", material: shadowMat }).position(-0.72, 0.055, -0.34).scale([0.44, 0.012, 0.28]),
270
+ primitives.cylinder({ name: "Aura3D gold robot contact shadow", material: shadowMat }).position(0.72, 0.055, -0.34).scale([0.44, 0.012, 0.28]),
271
+ primitives.box({ name: "Aura3D broom handle", material: bloomCyan }).position(-0.25, 0.28, -0.2).rotate(0, 0, -0.6).scale([0.58, 0.035, 0.035]),
272
+ primitives.box({ name: "Aura3D broom bristles", material: bloomGold }).position(-0.55, 0.14, -0.18).rotate(0, 0, -0.25).scale([0.18, 0.1, 0.04]),
273
+ ...createGlowStones(),
274
+ ...createMoonLilies(),
275
+ lights.ambient({ intensity: 0.2, color: "#b9f2ff" }),
276
+ lights.point({ name: "Aura3D moon garden cyan key", position: [-1.8, 2.4, 1.2], color: "#7de2ff", intensity: 2.8 }),
277
+ lights.point({ name: "Aura3D moon garden warm robot rim", position: [1.8, 1.7, 0.8], color: "#ffe18e", intensity: 2.0 })
278
+ ];
279
+ }
280
+
281
+ function createCartoonCitySilhouette() {
282
+ const mat = material.pbr({ color: "#071825", roughness: 0.88, metallic: 0.04 });
283
+ return [-2.2, -1.45, -0.62, 0.38, 1.25, 2.04].map((x, index) =>
284
+ primitives.box({ name: `Aura3D rounded distant city tower ${index + 1}`, material: mat })
285
+ .position(x, 0.45 + (index % 3) * 0.08, -1.72)
286
+ .scale([0.22 + (index % 2) * 0.08, 0.82 + (index % 3) * 0.18, 0.08])
287
+ );
288
+ }
289
+
290
+ function createGlowStones() {
291
+ const cyan = material.emissive({ color: "#7de2ff", emissive: "#7de2ff", emissiveIntensity: 1.2 });
292
+ const gold = material.emissive({ color: "#ffe18e", emissive: "#ffe18e", emissiveIntensity: 1.1 });
293
+ return [
294
+ primitives.sphere({ name: "Aura3D glow stone left foreground", material: gold }).position(-1.45, 0.1, 0.22).scale([0.1, 0.045, 0.08]),
295
+ primitives.sphere({ name: "Aura3D glow stone center", material: cyan }).position(-0.1, 0.1, 0.0).scale([0.08, 0.035, 0.06]),
296
+ primitives.sphere({ name: "Aura3D glow stone right foreground", material: gold }).position(1.48, 0.1, 0.18).scale([0.1, 0.045, 0.08])
297
+ ];
298
+ }
299
+
300
+ function createMoonLilies() {
301
+ const petal = material.emissive({ color: "#eafcff", emissive: "#d7fbff", emissiveIntensity: 0.48 });
302
+ const stem = material.emissive({ color: "#40ffbf", emissive: "#40ffbf", emissiveIntensity: 0.72 });
303
+ return [
304
+ primitives.cylinder({ name: "Aura3D moon lily stem left", material: stem }).position(-1.72, 0.22, -0.2).scale([0.018, 0.24, 0.018]),
305
+ primitives.sphere({ name: "Aura3D moon lily bloom left", material: petal }).position(-1.72, 0.42, -0.2).scale([0.16, 0.06, 0.12]),
306
+ primitives.cylinder({ name: "Aura3D moon lily stem right", material: stem }).position(1.76, 0.22, -0.26).scale([0.018, 0.24, 0.018]),
307
+ primitives.sphere({ name: "Aura3D moon lily bloom right", material: petal }).position(1.76, 0.42, -0.26).scale([0.16, 0.06, 0.12])
308
+ ];
309
+ }
310
+
293
311
  function sampleCartoonRouteAt(time: number) {
294
312
  const bridgeSample = sampleAuraVoiceBridgeAtTime(auraVoicePackage, time);
295
313
  const playbackSample = sampleShotPlaybackPlan(shotPlaybackPlan, time);