cozyclay 1.1.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +85 -0
- package/README.md +91 -25
- package/THIRD_PARTY_NOTICES.md +68 -1
- package/bin/cozyclay.mjs +71 -8
- package/dist/CNAME +1 -0
- package/dist/ai-camera-control/index.html +406 -0
- package/dist/app/index.html +57 -0
- package/dist/assets/app-B3U5aut1.js +4811 -0
- package/dist/assets/app-BrRF0wso.css +1 -0
- package/dist/assets/vision_bundle-jFkh-fIS.js +41 -0
- package/dist/favicon.ico +0 -0
- package/dist/fonts/InstrumentSerif-OFL.txt +93 -0
- package/dist/fonts/Inter-OFL.txt +92 -0
- package/dist/fonts/README.md +15 -0
- package/dist/icons/apple-touch-icon.png +0 -0
- package/dist/icons/icon-192.png +0 -0
- package/dist/icons/icon-32.png +0 -0
- package/dist/icons/icon-512.png +0 -0
- package/dist/icons/icon-maskable-512.png +0 -0
- package/dist/index.html +450 -5
- package/dist/manifest.webmanifest +42 -0
- package/dist/media/cozyclay-demo-poster.jpg +0 -0
- package/dist/media/cozyclay-logo.png +0 -0
- package/dist/robots.txt +4 -0
- package/dist/sitemap.xml +15 -0
- package/dist/sw.js +127 -0
- package/mcp/LIVE-PROTOCOL.md +63 -0
- package/mcp/README.md +142 -0
- package/mcp/ardy-prompts.mjs +170 -0
- package/mcp/live-hub.mjs +105 -0
- package/mcp/package.json +24 -0
- package/mcp/server.mjs +1394 -0
- package/package.json +122 -78
- package/src/App.jsx +4235 -1047
- package/src/ardy/client.js +5 -4
- package/src/ardy/cskel27.js +7 -2
- package/src/ardy/ik.js +25 -15
- package/src/ardy/mp4-duration.js +102 -0
- package/src/ardy/npz.js +64 -3
- package/src/ardy/playback.js +31 -1
- package/src/ardy/prompt-clips.js +7 -2
- package/src/ardy/retime.js +211 -0
- package/src/ardy/timeline-coordinates.js +24 -0
- package/src/ardy/timeline.jsx +650 -70
- package/src/ardy/to-cskel27.js +34 -12
- package/src/ardy/trim.js +33 -0
- package/src/ardy/waypoints.js +14 -9
- package/src/asset-pane.jsx +36 -0
- package/src/camera-block.js +60 -0
- package/src/camera-follow.js +49 -10
- package/src/camera-rail-schedule.js +176 -0
- package/src/controls.jsx +16 -2
- package/src/cuts.js +184 -0
- package/src/dualview.jsx +14 -8
- package/src/generation/client.js +56 -0
- package/src/generation/session.js +47 -0
- package/src/generation/shot-spec.js +40 -0
- package/src/generation/use-generation.js +55 -0
- package/src/hierarchy-model.js +104 -30
- package/src/hierarchy-panel.jsx +306 -26
- package/src/live-control.js +122 -0
- package/src/locale-toggle.jsx +18 -0
- package/src/locale.js +34 -0
- package/src/main.jsx +5 -0
- package/src/matte-editor.js +543 -0
- package/src/matte.js +503 -0
- package/src/multimodel-ingest.js +344 -0
- package/src/object-catalog.jsx +32 -3
- package/src/object-gizmo.jsx +43 -15
- package/src/planview.jsx +208 -89
- package/src/pose-extract/browser.js +42 -0
- package/src/pose-extract/detector.js +75 -0
- package/src/pose-extract/fit.js +194 -0
- package/src/pose-extract/index.js +56 -0
- package/src/pose-extract/keyframes.js +74 -0
- package/src/pose-extract/landmarks.js +162 -0
- package/src/pose-extract/take.js +87 -0
- package/src/pose-extract/video-frames.js +91 -0
- package/src/pose-thumbs.js +152 -0
- package/src/poses.js +114 -0
- package/src/posestudio.jsx +434 -22
- package/src/project-browser.jsx +135 -0
- package/src/project.js +289 -0
- package/src/props.jsx +69 -3
- package/src/pwa.js +42 -0
- package/src/result-modal.jsx +124 -0
- package/src/room.jsx +14 -35
- package/src/scene-asset-cache.js +125 -0
- package/src/scene-assets.js +288 -0
- package/src/scene-objects.js +245 -7
- package/src/scenes.js +404 -0
- package/src/shot-authoring.js +246 -77
- package/src/shot.js +8 -4
- package/src/styles.css +2151 -183
- package/src/ui.jsx +54 -5
- package/tools/ardy/BRIDGE.md +73 -6
- package/tools/ardy/README.md +64 -5
- package/tools/ardy/bridge.mjs +146 -390
- package/tools/ardy/bvh-cskel27.mjs +1209 -0
- package/tools/ardy/cclay_constrained_generate.py +561 -0
- package/tools/ardy/cclay_motion_edit.py +74 -9
- package/tools/ardy/cclay_sequence_generate.py +117 -7
- package/tools/ardy/cclay_worker.py +304 -0
- package/tools/ardy/extract.mjs +367 -0
- package/tools/ardy/footage.mjs +462 -0
- package/tools/ardy/merge-text-encoder.py +160 -0
- package/tools/ardy/npz.mjs +74 -9
- package/tools/ardy/run-local.mjs +233 -0
- package/tools/ardy/run-on-box.sh +25 -0
- package/tools/ardy/run-sequence-on-box.sh +15 -0
- package/tools/ardy/runners/index.mjs +23 -0
- package/tools/ardy/runners/local.mjs +452 -0
- package/tools/ardy/runners/proc.mjs +125 -0
- package/tools/ardy/runners/remote.mjs +252 -0
- package/tools/ardy/setup-local.mjs +189 -0
- package/tools/ardy/setup-text-encoder-on-box.sh +93 -0
- package/tools/ardy/setup-text-encoder.py +296 -0
- package/tools/generation/README.md +29 -0
- package/tools/generation/bridge.mjs +149 -0
- package/tools/generation/job-store.mjs +51 -0
- package/tools/generation/providers/kling.mjs +76 -0
- package/tools/generation/providers/runway.mjs +101 -0
- package/tools/generation/providers/seedance.mjs +87 -0
- package/tools/generation/providers/shared.mjs +49 -0
- package/tools/generation/providers/veo.mjs +84 -0
- package/tools/metrics/archive-traffic.mjs +138 -0
- package/dist/assets/index-BW_S1YRy.js +0 -4395
- package/dist/assets/index-nRmmJgap.css +0 -1
- package/tools/ardy/__pycache__/cclay_sequence_generate.cpython-313.pyc +0 -0
- package/tools/ardy/out/gen-1786443326924-c6019e-generated.npz +0 -0
- package/tools/ardy/out/gen-1786443609325-0053db-generated.npz +0 -0
- package/tools/ardy/out/gen-1786443835628-749ed1-generated.npz +0 -0
- package/tools/ardy/out/gen-1786462605247-03cb19-generated.npz +0 -0
- package/tools/qa-browser.mjs +0 -84
- package/tools/qa-crop3.mjs +0 -22
- package/tools/qa-playview.mjs +0 -49
- package/tools/qa-screenshot.mjs +0 -24
- package/tools/qa-visual.mjs +0 -51
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
|
|
6
|
+
<meta name="description" content="Camera control for AI video: why prompting an angle keeps drifting, what Seedance, Veo and Kling actually accept, and how blocking the shot in 3D first gives you the same camera on every take." />
|
|
7
|
+
<meta name="theme-color" content="#232323" />
|
|
8
|
+
<link rel="canonical" href="https://cozyclay.org/ai-camera-control/" />
|
|
9
|
+
<link rel="icon" href="/favicon.ico" sizes="any" />
|
|
10
|
+
<link rel="icon" type="image/png" sizes="32x32" href="/icons/icon-32.png" />
|
|
11
|
+
<link rel="icon" type="image/png" sizes="192x192" href="/icons/icon-192.png" />
|
|
12
|
+
<link rel="apple-touch-icon" href="/icons/apple-touch-icon.png" />
|
|
13
|
+
|
|
14
|
+
<meta property="og:type" content="article" />
|
|
15
|
+
<meta property="og:site_name" content="CozyClay" />
|
|
16
|
+
<meta property="og:title" content="Camera control for AI video: block the shot instead of describing it" />
|
|
17
|
+
<meta property="og:description" content="Why camera prompts drift between takes, and how blocking the angle, lens and dolly in 3D first gets you the same shot every time." />
|
|
18
|
+
<meta property="og:url" content="https://cozyclay.org/ai-camera-control/" />
|
|
19
|
+
<meta property="og:image" content="https://cozyclay.org/media/cozyclay-demo-poster.jpg" />
|
|
20
|
+
<meta property="og:image:width" content="1920" />
|
|
21
|
+
<meta property="og:image:height" content="1080" />
|
|
22
|
+
<meta name="twitter:card" content="summary_large_image" />
|
|
23
|
+
<meta name="twitter:title" content="Camera control for AI video: block the shot instead of describing it" />
|
|
24
|
+
<meta name="twitter:description" content="Why camera prompts drift between takes, and how blocking the angle, lens and dolly in 3D first gets you the same shot every time." />
|
|
25
|
+
<meta name="twitter:image" content="https://cozyclay.org/media/cozyclay-demo-poster.jpg" />
|
|
26
|
+
|
|
27
|
+
<title>Camera control for AI video — block the shot, don't describe it</title>
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
<script type="application/ld+json">
|
|
31
|
+
{
|
|
32
|
+
"@context": "https://schema.org",
|
|
33
|
+
"@type": "Article",
|
|
34
|
+
"headline": "Camera control for AI video: block the shot instead of describing it",
|
|
35
|
+
"description": "Why camera prompts drift between takes, and how blocking the angle, lens and dolly in 3D first gets you the same shot every time.",
|
|
36
|
+
"image": "https://cozyclay.org/media/cozyclay-demo-poster.jpg",
|
|
37
|
+
"mainEntityOfPage": "https://cozyclay.org/ai-camera-control/",
|
|
38
|
+
"author": {
|
|
39
|
+
"@type": "Organization",
|
|
40
|
+
"name": "CozyClay",
|
|
41
|
+
"url": "https://cozyclay.org/"
|
|
42
|
+
},
|
|
43
|
+
"publisher": {
|
|
44
|
+
"@type": "Organization",
|
|
45
|
+
"name": "CozyClay",
|
|
46
|
+
"logo": {
|
|
47
|
+
"@type": "ImageObject",
|
|
48
|
+
"url": "https://cozyclay.org/media/cozyclay-logo.png"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
</script>
|
|
53
|
+
<script type="application/ld+json">
|
|
54
|
+
{
|
|
55
|
+
"@context": "https://schema.org",
|
|
56
|
+
"@type": "FAQPage",
|
|
57
|
+
"mainEntity": [
|
|
58
|
+
{
|
|
59
|
+
"@type": "Question",
|
|
60
|
+
"name": "Why does my camera angle change between takes?",
|
|
61
|
+
"acceptedAnswer": {
|
|
62
|
+
"@type": "Answer",
|
|
63
|
+
"text": "Because the prompt never fixed it. Words like low angle or close up carry no height, distance or focal length, so the model fills those in and fills them in differently on each run. Pinning composition needs something with numbers behind it - a starting frame, or a camera that exists in a scene."
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"@type": "Question",
|
|
68
|
+
"name": "Do camera movement prompts work at all?",
|
|
69
|
+
"acceptedAnswer": {
|
|
70
|
+
"@type": "Answer",
|
|
71
|
+
"text": "They work as a nudge. Slow dolly in reliably gets you a push rather than a pan. What it will not do is get you the same push twice, or a push that matches the one in the shot you are cutting to."
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"@type": "Question",
|
|
76
|
+
"name": "What is the highest-leverage thing I can control in AI video?",
|
|
77
|
+
"acceptedAnswer": {
|
|
78
|
+
"@type": "Answer",
|
|
79
|
+
"text": "The first frame. Composition, lens compression and eyeline are all decided the moment that image exists, and the generation inherits them instead of guessing. This is what blocking a shot in 3D gives you."
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
"@type": "Question",
|
|
84
|
+
"name": "Do I need Blender or Unreal to block a shot?",
|
|
85
|
+
"acceptedAnswer": {
|
|
86
|
+
"@type": "Answer",
|
|
87
|
+
"text": "No. Both will do it, and both ask you to learn a full DCC to move one camera. CozyClay does the blocking part in a browser tab with Unity-style controls."
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
]
|
|
91
|
+
}
|
|
92
|
+
</script>
|
|
93
|
+
|
|
94
|
+
<script defer src="https://static.cloudflareinsights.com/beacon.min.js" data-cf-beacon='{"token": "6a4384d1375d4ffc8a30a6346a8ad1a0"}'></script>
|
|
95
|
+
<style>
|
|
96
|
+
:root {
|
|
97
|
+
--bg: #1b1b1b;
|
|
98
|
+
--panel: #232323;
|
|
99
|
+
--line: #343434;
|
|
100
|
+
--ink: #ece7de;
|
|
101
|
+
--muted: #a9a29a;
|
|
102
|
+
--clay: #f2eee6;
|
|
103
|
+
--accent: #d9a066;
|
|
104
|
+
}
|
|
105
|
+
* { box-sizing: border-box; }
|
|
106
|
+
html { scroll-behavior: smooth; }
|
|
107
|
+
body {
|
|
108
|
+
margin: 0;
|
|
109
|
+
background: var(--bg);
|
|
110
|
+
color: var(--ink);
|
|
111
|
+
font: 16px/1.65 ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
112
|
+
-webkit-font-smoothing: antialiased;
|
|
113
|
+
}
|
|
114
|
+
a { color: var(--accent); }
|
|
115
|
+
.wrap { max-width: 980px; margin: 0 auto; padding: 0 20px; }
|
|
116
|
+
header.site {
|
|
117
|
+
border-bottom: 1px solid var(--line);
|
|
118
|
+
position: sticky; top: 0; background: rgba(27,27,27,.9);
|
|
119
|
+
backdrop-filter: blur(8px); z-index: 10;
|
|
120
|
+
}
|
|
121
|
+
header.site .wrap {
|
|
122
|
+
display: flex; align-items: center; gap: 16px;
|
|
123
|
+
min-height: 60px; flex-wrap: wrap;
|
|
124
|
+
}
|
|
125
|
+
.brand { font-weight: 700; letter-spacing: -.01em; color: var(--clay); text-decoration: none; font-size: 18px; }
|
|
126
|
+
header.site nav { margin-left: auto; display: flex; gap: 18px; flex-wrap: wrap; }
|
|
127
|
+
header.site nav a { color: var(--muted); text-decoration: none; font-size: 14px; }
|
|
128
|
+
header.site nav a:hover { color: var(--ink); }
|
|
129
|
+
|
|
130
|
+
.hero { padding: 64px 0 40px; }
|
|
131
|
+
h1 { font-size: clamp(30px, 5.2vw, 46px); line-height: 1.12; margin: 0 0 18px; letter-spacing: -.02em; color: var(--clay); }
|
|
132
|
+
.lede { font-size: clamp(17px, 2.2vw, 20px); color: var(--muted); margin: 0 0 28px; max-width: 44em; }
|
|
133
|
+
.cta { display: flex; gap: 12px; flex-wrap: wrap; margin-bottom: 14px; }
|
|
134
|
+
.btn {
|
|
135
|
+
display: inline-block; padding: 12px 20px; border-radius: 8px;
|
|
136
|
+
text-decoration: none; font-weight: 600; font-size: 15px; border: 1px solid var(--line);
|
|
137
|
+
}
|
|
138
|
+
.btn.primary { background: var(--clay); color: #1b1b1b; border-color: var(--clay); }
|
|
139
|
+
.btn.ghost { color: var(--ink); }
|
|
140
|
+
.note { color: var(--muted); font-size: 14px; margin: 0; }
|
|
141
|
+
|
|
142
|
+
figure { margin: 40px 0; }
|
|
143
|
+
figure img { width: 100%; height: auto; display: block; border: 1px solid var(--line); border-radius: 10px; }
|
|
144
|
+
figcaption { color: var(--muted); font-size: 13px; margin-top: 10px; }
|
|
145
|
+
|
|
146
|
+
/* The mark is blue on white and the eyes are knocked out of the fill, so
|
|
147
|
+
it sits on a light plate rather than straight on the dark page. */
|
|
148
|
+
.mark { margin: 0 0 26px; max-width: 190px; }
|
|
149
|
+
.mark img {
|
|
150
|
+
border: 0;
|
|
151
|
+
border-radius: 14px;
|
|
152
|
+
background: var(--clay);
|
|
153
|
+
padding: 14px 12px;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
.reel {
|
|
157
|
+
margin: 0 0 30px;
|
|
158
|
+
border: 1px solid var(--line);
|
|
159
|
+
border-radius: 12px;
|
|
160
|
+
overflow: hidden;
|
|
161
|
+
background: #000;
|
|
162
|
+
box-shadow: 0 24px 60px rgba(0, 0, 0, .45);
|
|
163
|
+
}
|
|
164
|
+
.reel video { display: block; width: 100%; height: auto; }
|
|
165
|
+
.reel-note { color: var(--muted); font-size: 13px; margin: 12px 0 0; }
|
|
166
|
+
|
|
167
|
+
section { padding: 40px 0; border-top: 1px solid var(--line); }
|
|
168
|
+
h2 { font-size: clamp(22px, 3.2vw, 28px); margin: 0 0 8px; color: var(--clay); letter-spacing: -.01em; }
|
|
169
|
+
h3 { font-size: 17px; margin: 0 0 6px; color: var(--clay); }
|
|
170
|
+
.sub { color: var(--muted); margin: 0 0 26px; max-width: 48em; }
|
|
171
|
+
|
|
172
|
+
.grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 18px; }
|
|
173
|
+
.card { background: var(--panel); border: 1px solid var(--line); border-radius: 10px; padding: 20px; }
|
|
174
|
+
.card p { margin: 0; color: var(--muted); font-size: 15px; }
|
|
175
|
+
|
|
176
|
+
pre {
|
|
177
|
+
background: var(--panel); border: 1px solid var(--line); border-radius: 10px;
|
|
178
|
+
padding: 16px 18px; overflow-x: auto; font-size: 14px; line-height: 1.7;
|
|
179
|
+
}
|
|
180
|
+
code { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
|
|
181
|
+
|
|
182
|
+
table { width: 100%; border-collapse: collapse; font-size: 15px; }
|
|
183
|
+
th, td { text-align: left; padding: 10px 12px; border-bottom: 1px solid var(--line); }
|
|
184
|
+
th { color: var(--clay); font-weight: 600; white-space: nowrap; }
|
|
185
|
+
td { color: var(--muted); }
|
|
186
|
+
.scroller { overflow-x: auto; }
|
|
187
|
+
|
|
188
|
+
details { border-bottom: 1px solid var(--line); padding: 16px 0; }
|
|
189
|
+
details summary { cursor: pointer; font-weight: 600; color: var(--clay); font-size: 16px; }
|
|
190
|
+
details p { color: var(--muted); margin: 12px 0 0; }
|
|
191
|
+
|
|
192
|
+
footer.site { border-top: 1px solid var(--line); padding: 32px 0 56px; color: var(--muted); font-size: 14px; }
|
|
193
|
+
footer.site a { color: var(--muted); }
|
|
194
|
+
|
|
195
|
+
@media (max-width: 620px) {
|
|
196
|
+
.hero { padding: 40px 0 28px; }
|
|
197
|
+
.btn { width: 100%; text-align: center; }
|
|
198
|
+
header.site nav { gap: 14px; width: 100%; margin-left: 0; padding-bottom: 10px; }
|
|
199
|
+
}
|
|
200
|
+
</style>
|
|
201
|
+
</head>
|
|
202
|
+
<body>
|
|
203
|
+
<header class="site">
|
|
204
|
+
<div class="wrap">
|
|
205
|
+
<a class="brand" href="/">CozyClay</a>
|
|
206
|
+
<nav>
|
|
207
|
+
<a href="/#what">What it does</a>
|
|
208
|
+
<a href="/#start">Quick start</a>
|
|
209
|
+
<a href="/ai-camera-control/">AI camera control</a>
|
|
210
|
+
<a href="https://github.com/HaD0Yun/CozyClay">GitHub</a>
|
|
211
|
+
</nav>
|
|
212
|
+
</div>
|
|
213
|
+
</header>
|
|
214
|
+
|
|
215
|
+
<main class="wrap">
|
|
216
|
+
<div class="hero">
|
|
217
|
+
<h1>Camera control for AI video</h1>
|
|
218
|
+
<p class="lede">
|
|
219
|
+
Video models will happily give you a low angle. They will not give you
|
|
220
|
+
<em>the same</em> low angle twice. Here is why the prompt keeps drifting, and what
|
|
221
|
+
to do about it.
|
|
222
|
+
</p>
|
|
223
|
+
</div>
|
|
224
|
+
|
|
225
|
+
<section>
|
|
226
|
+
<h2>The part nobody warns you about</h2>
|
|
227
|
+
<p class="sub">
|
|
228
|
+
A prompt describes a camera in words, and words have no units. "Low angle, close on
|
|
229
|
+
her face, slow push in" is four decisions with no numbers attached: how low, how
|
|
230
|
+
close, what focal length, how far the push travels. The model picks values you never
|
|
231
|
+
specified, and it picks different ones on the next run.
|
|
232
|
+
</p>
|
|
233
|
+
<p class="sub">
|
|
234
|
+
That is fine for a single hero shot. It falls apart the moment you need two shots to
|
|
235
|
+
belong to the same scene. Cut between two takes that each invented their own lens and
|
|
236
|
+
eyeline and the geometry does not agree — the room changes shape across the cut, and
|
|
237
|
+
the audience feels it even when they cannot name it.
|
|
238
|
+
</p>
|
|
239
|
+
<p class="sub">
|
|
240
|
+
The usual response is to write a longer prompt. More adjectives, a reference image,
|
|
241
|
+
a list of shot-type vocabulary. It narrows the spread. It does not close it, because
|
|
242
|
+
the thing being specified was never language in the first place.
|
|
243
|
+
</p>
|
|
244
|
+
</section>
|
|
245
|
+
|
|
246
|
+
<section>
|
|
247
|
+
<h2>What the models actually take</h2>
|
|
248
|
+
<p class="sub">
|
|
249
|
+
The current generation of video models accepts camera intent through some mix of
|
|
250
|
+
three channels, and it is worth knowing which one you are using:
|
|
251
|
+
</p>
|
|
252
|
+
<div class="grid">
|
|
253
|
+
<div class="card">
|
|
254
|
+
<h3>Words</h3>
|
|
255
|
+
<p>
|
|
256
|
+
Shot-type vocabulary in the prompt — wide, medium, close, dolly, crane, 35mm.
|
|
257
|
+
Universally supported, and the loosest. The model interprets; you do not specify.
|
|
258
|
+
</p>
|
|
259
|
+
</div>
|
|
260
|
+
<div class="card">
|
|
261
|
+
<h3>A first frame</h3>
|
|
262
|
+
<p>
|
|
263
|
+
An image the generation starts from. This pins composition hard, because framing,
|
|
264
|
+
lens compression and eyeline are already baked into the pixels. It is the highest
|
|
265
|
+
leverage input most people underuse.
|
|
266
|
+
</p>
|
|
267
|
+
</div>
|
|
268
|
+
<div class="card">
|
|
269
|
+
<h3>A path or reference clip</h3>
|
|
270
|
+
<p>
|
|
271
|
+
Some models take an explicit camera path, or a video whose motion is transferred
|
|
272
|
+
onto a new scene. Strongest control, narrowest support, and it needs the move to
|
|
273
|
+
exist somewhere before you can hand it over.
|
|
274
|
+
</p>
|
|
275
|
+
</div>
|
|
276
|
+
</div>
|
|
277
|
+
</section>
|
|
278
|
+
|
|
279
|
+
<section>
|
|
280
|
+
<h2>Block the shot, then generate</h2>
|
|
281
|
+
<p class="sub">
|
|
282
|
+
Film production solved this before any of these models existed, and the answer was
|
|
283
|
+
previs: build the scene rough in 3D, put a camera in it, and settle the shot while
|
|
284
|
+
it is still cheap to change. The blocking is the specification. Everything downstream
|
|
285
|
+
inherits it.
|
|
286
|
+
</p>
|
|
287
|
+
<p class="sub">
|
|
288
|
+
The same move works here. Put the geometry somewhere real first — a boxed-out set, a
|
|
289
|
+
character on a path, a camera with an actual position and an actual focal length —
|
|
290
|
+
and the questions the prompt was leaving open are already answered. You are no longer
|
|
291
|
+
asking for a low angle. You have one, and you can hand over the frame it produces.
|
|
292
|
+
</p>
|
|
293
|
+
<p class="sub">
|
|
294
|
+
Three things change once the camera lives in a scene instead of a sentence:
|
|
295
|
+
</p>
|
|
296
|
+
<div class="grid">
|
|
297
|
+
<div class="card">
|
|
298
|
+
<h3>Shots agree with each other</h3>
|
|
299
|
+
<p>
|
|
300
|
+
Two angles on one blocked set share a floor plan, so a cut between them holds.
|
|
301
|
+
Two prompts do not share anything.
|
|
302
|
+
</p>
|
|
303
|
+
</div>
|
|
304
|
+
<div class="card">
|
|
305
|
+
<h3>The move is reproducible</h3>
|
|
306
|
+
<p>
|
|
307
|
+
A dolly you drew is the same dolly tomorrow. Regenerate, change the subject, keep
|
|
308
|
+
the camera. Nothing about that survives a reroll of a text prompt.
|
|
309
|
+
</p>
|
|
310
|
+
</div>
|
|
311
|
+
<div class="card">
|
|
312
|
+
<h3>You fail cheaply</h3>
|
|
313
|
+
<p>
|
|
314
|
+
Finding out the angle was wrong costs a drag in a viewport, not a paid generation
|
|
315
|
+
and the wait attached to it.
|
|
316
|
+
</p>
|
|
317
|
+
</div>
|
|
318
|
+
</div>
|
|
319
|
+
</section>
|
|
320
|
+
|
|
321
|
+
<section>
|
|
322
|
+
<h2>Doing it in the browser</h2>
|
|
323
|
+
<p class="sub">
|
|
324
|
+
CozyClay is the previs half of that loop, built to be opened rather than installed.
|
|
325
|
+
Block a set out of primitives, drop a character and draw the path they walk, place
|
|
326
|
+
the camera and set its lens, cut between shots on a timeline, and scrub until the
|
|
327
|
+
timing is right. Then take those frames and those camera moves to whichever model you
|
|
328
|
+
are generating with.
|
|
329
|
+
</p>
|
|
330
|
+
|
|
331
|
+
<div class="reel">
|
|
332
|
+
<video
|
|
333
|
+
src="/media/cozyclay-demo.mp4"
|
|
334
|
+
poster="/media/cozyclay-demo-poster.jpg"
|
|
335
|
+
width="1920" height="1080"
|
|
336
|
+
muted loop playsinline controls preload="none"
|
|
337
|
+
></video>
|
|
338
|
+
</div>
|
|
339
|
+
<p class="reel-note">
|
|
340
|
+
Left of the wipe is the generated result. Right of it is the blocking that produced
|
|
341
|
+
the camera.
|
|
342
|
+
</p>
|
|
343
|
+
|
|
344
|
+
<div class="cta">
|
|
345
|
+
<a class="btn primary" href="/">What CozyClay does</a>
|
|
346
|
+
<a class="btn ghost" href="https://github.com/HaD0Yun/CozyClay">View on GitHub</a>
|
|
347
|
+
</div>
|
|
348
|
+
<p class="note">
|
|
349
|
+
Free and open source. Runs locally with <code>npx cozyclay</code>.
|
|
350
|
+
</p>
|
|
351
|
+
</section>
|
|
352
|
+
|
|
353
|
+
<section id="faq">
|
|
354
|
+
<h2>FAQ</h2>
|
|
355
|
+
<details open>
|
|
356
|
+
<summary>Why does my camera angle change between takes?</summary>
|
|
357
|
+
<p>
|
|
358
|
+
Because the prompt never fixed it. Words like "low angle" or "close up" carry no
|
|
359
|
+
height, distance or focal length, so the model fills those in and fills them in
|
|
360
|
+
differently on each run. Pinning composition needs something with numbers behind it
|
|
361
|
+
— a starting frame, or a camera that exists in a scene.
|
|
362
|
+
</p>
|
|
363
|
+
</details>
|
|
364
|
+
<details>
|
|
365
|
+
<summary>Do camera movement prompts work at all?</summary>
|
|
366
|
+
<p>
|
|
367
|
+
They work as a nudge. "Slow dolly in" reliably gets you a push rather than a pan.
|
|
368
|
+
What it will not do is get you the same push twice, or a push that matches the one
|
|
369
|
+
in the shot you are cutting to. Use the vocabulary, but do not expect it to carry
|
|
370
|
+
continuity.
|
|
371
|
+
</p>
|
|
372
|
+
</details>
|
|
373
|
+
<details>
|
|
374
|
+
<summary>What is the highest-leverage thing I can control?</summary>
|
|
375
|
+
<p>
|
|
376
|
+
The first frame. Composition, lens compression and eyeline are all decided the
|
|
377
|
+
moment that image exists, and the generation inherits them instead of guessing.
|
|
378
|
+
This is exactly what blocking a shot in 3D gives you.
|
|
379
|
+
</p>
|
|
380
|
+
</details>
|
|
381
|
+
<details>
|
|
382
|
+
<summary>Do I need Blender or Unreal to block a shot?</summary>
|
|
383
|
+
<p>
|
|
384
|
+
No. Both will do it, and both ask you to learn a full DCC to move one camera.
|
|
385
|
+
CozyClay does the blocking part in a browser tab with Unity-style controls —
|
|
386
|
+
see <a href="/">what it does</a>.
|
|
387
|
+
</p>
|
|
388
|
+
</details>
|
|
389
|
+
</section>
|
|
390
|
+
</main>
|
|
391
|
+
|
|
392
|
+
<footer class="site">
|
|
393
|
+
<div class="wrap">
|
|
394
|
+
<p>
|
|
395
|
+
<a href="/">CozyClay</a> ·
|
|
396
|
+
<a href="https://github.com/HaD0Yun/CozyClay">GitHub</a> ·
|
|
397
|
+
<a href="https://www.npmjs.com/package/cozyclay">npm</a>
|
|
398
|
+
</p>
|
|
399
|
+
<p>
|
|
400
|
+
CozyClay is not affiliated with or endorsed by NVIDIA, Unity Technologies, ByteDance,
|
|
401
|
+
Google or Kuaishou. Product names are used only to describe interoperability.
|
|
402
|
+
</p>
|
|
403
|
+
</div>
|
|
404
|
+
</footer>
|
|
405
|
+
</body>
|
|
406
|
+
</html>
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
|
|
6
|
+
<meta name="description" content="CozyClay studio: block a 3D scene, pose characters, author camera moves, and preview generated motion in your browser." />
|
|
7
|
+
<meta name="theme-color" content="#232323" />
|
|
8
|
+
<meta name="apple-mobile-web-app-capable" content="yes" />
|
|
9
|
+
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
|
10
|
+
<meta name="apple-mobile-web-app-title" content="CozyClay" />
|
|
11
|
+
<!-- Unlinked on purpose: the studio is no longer promoted as a hosted demo.
|
|
12
|
+
It stays reachable so existing PWA installs keep working, but it should
|
|
13
|
+
not collect search traffic. -->
|
|
14
|
+
<meta name="robots" content="noindex, follow" />
|
|
15
|
+
<link rel="canonical" href="https://cozyclay.org/app/" />
|
|
16
|
+
<link rel="manifest" href="/manifest.webmanifest" />
|
|
17
|
+
<link rel="icon" href="/favicon.ico" sizes="any" />
|
|
18
|
+
<link rel="icon" type="image/png" sizes="32x32" href="/icons/icon-32.png" />
|
|
19
|
+
<link rel="icon" type="image/png" sizes="192x192" href="/icons/icon-192.png" />
|
|
20
|
+
<link rel="apple-touch-icon" href="/icons/apple-touch-icon.png" />
|
|
21
|
+
|
|
22
|
+
<meta property="og:type" content="website" />
|
|
23
|
+
<meta property="og:site_name" content="CozyClay" />
|
|
24
|
+
<meta property="og:title" content="CozyClay Studio — 3D staging and previs in the browser" />
|
|
25
|
+
<meta property="og:description" content="Block a scene, pose characters, author camera moves, and preview generated motion. No install, no Blender, no Unity." />
|
|
26
|
+
<meta property="og:url" content="https://cozyclay.org/app/" />
|
|
27
|
+
<meta property="og:image" content="https://cozyclay.org/media/cozyclay-demo-poster.jpg" />
|
|
28
|
+
<meta property="og:image:width" content="1920" />
|
|
29
|
+
<meta property="og:image:height" content="1080" />
|
|
30
|
+
<meta name="twitter:card" content="summary_large_image" />
|
|
31
|
+
<meta name="twitter:title" content="CozyClay Studio — 3D staging and previs in the browser" />
|
|
32
|
+
<meta name="twitter:description" content="Block a scene, pose characters, author camera moves, and preview generated motion. No install, no Blender, no Unity." />
|
|
33
|
+
<meta name="twitter:image" content="https://cozyclay.org/media/cozyclay-demo-poster.jpg" />
|
|
34
|
+
|
|
35
|
+
<title>CozyClay Studio — 3D staging and previs in your browser</title>
|
|
36
|
+
|
|
37
|
+
<script defer src="https://static.cloudflareinsights.com/beacon.min.js" data-cf-beacon='{"token": "6a4384d1375d4ffc8a30a6346a8ad1a0"}'></script>
|
|
38
|
+
<script type="module" crossorigin src="/assets/app-B3U5aut1.js"></script>
|
|
39
|
+
<link rel="stylesheet" crossorigin href="/assets/app-BrRF0wso.css">
|
|
40
|
+
</head>
|
|
41
|
+
<body>
|
|
42
|
+
<div id="root">
|
|
43
|
+
<noscript>
|
|
44
|
+
<h1>CozyClay Studio</h1>
|
|
45
|
+
<p>
|
|
46
|
+
CozyClay is a browser-based 3D staging studio. Block a scene, pose characters,
|
|
47
|
+
author camera moves and cuts, sequence motion prompts on a timeline, and preview
|
|
48
|
+
generated motion — in one local workspace that handles like the Unity Editor.
|
|
49
|
+
</p>
|
|
50
|
+
<p>
|
|
51
|
+
The studio needs JavaScript and WebGL. Enable JavaScript to load it, or read
|
|
52
|
+
<a href="/">what CozyClay does</a> first.
|
|
53
|
+
</p>
|
|
54
|
+
</noscript>
|
|
55
|
+
</div>
|
|
56
|
+
</body>
|
|
57
|
+
</html>
|