frameyard 0.3.0 → 0.5.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 +117 -0
- package/GLOSSARY.md +46 -2
- package/README.md +174 -106
- package/dist/bindings.d.ts +42 -0
- package/dist/bindings.d.ts.map +1 -0
- package/dist/bindings.js +144 -0
- package/dist/bindings.js.map +1 -0
- package/dist/cli.js +32 -21
- package/dist/cli.js.map +1 -1
- package/dist/composition/adapter.d.ts +30 -0
- package/dist/composition/adapter.d.ts.map +1 -0
- package/dist/composition/adapter.js +2 -0
- package/dist/composition/adapter.js.map +1 -0
- package/dist/composition/animation.d.ts +19 -0
- package/dist/composition/animation.d.ts.map +1 -0
- package/dist/composition/animation.js +55 -0
- package/dist/composition/animation.js.map +1 -0
- package/dist/composition/attributes.d.ts +7 -0
- package/dist/composition/attributes.d.ts.map +1 -0
- package/dist/composition/attributes.js +24 -0
- package/dist/composition/attributes.js.map +1 -0
- package/dist/composition/descriptor.d.ts +30 -0
- package/dist/composition/descriptor.d.ts.map +1 -0
- package/dist/composition/descriptor.js +188 -0
- package/dist/composition/descriptor.js.map +1 -0
- package/dist/composition/diagnostics.d.ts +12 -0
- package/dist/composition/diagnostics.d.ts.map +1 -0
- package/dist/composition/diagnostics.js +38 -0
- package/dist/composition/diagnostics.js.map +1 -0
- package/dist/composition/index.d.ts +12 -0
- package/dist/composition/index.d.ts.map +1 -0
- package/dist/composition/index.js +7 -0
- package/dist/composition/index.js.map +1 -0
- package/dist/composition/media.d.ts +27 -0
- package/dist/composition/media.d.ts.map +1 -0
- package/dist/composition/media.js +162 -0
- package/dist/composition/media.js.map +1 -0
- package/dist/composition/paths.d.ts +5 -0
- package/dist/composition/paths.d.ts.map +1 -0
- package/dist/composition/paths.js +16 -0
- package/dist/composition/paths.js.map +1 -0
- package/dist/composition/transcripts.d.ts +19 -0
- package/dist/composition/transcripts.d.ts.map +1 -0
- package/dist/composition/transcripts.js +57 -0
- package/dist/composition/transcripts.js.map +1 -0
- package/dist/composition/walk.d.ts +10 -0
- package/dist/composition/walk.d.ts.map +1 -0
- package/dist/composition/walk.js +32 -0
- package/dist/composition/walk.js.map +1 -0
- package/dist/contracts.d.ts +75 -47
- package/dist/contracts.d.ts.map +1 -1
- package/dist/contracts.js +14 -159
- package/dist/contracts.js.map +1 -1
- package/dist/errors.d.ts +15 -4
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +8 -0
- package/dist/errors.js.map +1 -1
- package/dist/html/document-adapter.d.ts +5 -0
- package/dist/html/document-adapter.d.ts.map +1 -0
- package/dist/html/document-adapter.js +17 -0
- package/dist/html/document-adapter.js.map +1 -0
- package/dist/html/index.d.ts +5 -0
- package/dist/html/index.d.ts.map +1 -0
- package/dist/html/index.js +3 -0
- package/dist/html/index.js.map +1 -0
- package/dist/html/parse.d.ts +26 -0
- package/dist/html/parse.d.ts.map +1 -0
- package/dist/html/parse.js +80 -0
- package/dist/html/parse.js.map +1 -0
- package/dist/html/validate.d.ts +14 -0
- package/dist/html/validate.d.ts.map +1 -0
- package/dist/html/validate.js +648 -0
- package/dist/html/validate.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/preview/main.d.ts +10 -3
- package/dist/preview/main.d.ts.map +1 -1
- package/dist/preview/main.js +189 -89
- package/dist/preview/main.js.map +1 -1
- package/dist/project.d.ts +2 -0
- package/dist/project.d.ts.map +1 -1
- package/dist/project.js +4 -0
- package/dist/project.js.map +1 -1
- package/dist/renderer/browser.d.ts +11 -1
- package/dist/renderer/browser.d.ts.map +1 -1
- package/dist/renderer/browser.js +93 -14
- package/dist/renderer/browser.js.map +1 -1
- package/dist/renderer/index.d.ts +25 -5
- package/dist/renderer/index.d.ts.map +1 -1
- package/dist/renderer/index.js +82 -16
- package/dist/renderer/index.js.map +1 -1
- package/dist/runtime/bindings.d.ts +11 -0
- package/dist/runtime/bindings.d.ts.map +1 -0
- package/dist/runtime/bindings.js +164 -0
- package/dist/runtime/bindings.js.map +1 -0
- package/dist/runtime/diagnostics.d.ts +4 -0
- package/dist/runtime/diagnostics.d.ts.map +1 -0
- package/dist/runtime/diagnostics.js +107 -0
- package/dist/runtime/diagnostics.js.map +1 -0
- package/dist/runtime/document-adapter.d.ts +4 -0
- package/dist/runtime/document-adapter.d.ts.map +1 -0
- package/dist/runtime/document-adapter.js +19 -0
- package/dist/runtime/document-adapter.js.map +1 -0
- package/dist/runtime/host.d.ts +14 -0
- package/dist/runtime/host.d.ts.map +1 -0
- package/dist/runtime/host.js +596 -0
- package/dist/runtime/host.js.map +1 -0
- package/dist/runtime/index.d.ts +0 -29
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/runtime/index.js +0 -360
- package/dist/runtime/index.js.map +1 -1
- package/dist/runtime/media.d.ts.map +1 -1
- package/dist/runtime/media.js +20 -51
- package/dist/runtime/media.js.map +1 -1
- package/dist/scaffold.d.ts.map +1 -1
- package/dist/scaffold.js +177 -84
- package/dist/scaffold.js.map +1 -1
- package/dist/server/composition-html.d.ts +20 -0
- package/dist/server/composition-html.d.ts.map +1 -0
- package/dist/server/composition-html.js +45 -0
- package/dist/server/composition-html.js.map +1 -0
- package/dist/server/preview-html.d.ts.map +1 -1
- package/dist/server/preview-html.js +19 -14
- package/dist/server/preview-html.js.map +1 -1
- package/dist/server/server.d.ts.map +1 -1
- package/dist/server/server.js +94 -6
- package/dist/server/server.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/docs/adr/0001-keep-composition-authoring-web-native.md +2 -0
- package/docs/adr/0006-seek-embedded-media-from-the-frame-clock.md +7 -1
- package/docs/adr/0007-make-html-documents-the-composition-unit.md +31 -0
- package/docs/adr/0008-share-the-composition-document-grammar.md +18 -0
- package/docs/adr/0009-composite-production-frames-in-software.md +37 -0
- package/docs/migration-0.4.md +279 -0
- package/docs/v0.1-design.md +78 -56
- package/examples/demo/README.md +8 -4
- package/examples/demo/compositions/demo.controller.js +120 -0
- package/examples/demo/compositions/demo.html +253 -0
- package/examples/demo/video.config.js +1 -1
- package/examples/video/README.md +61 -0
- package/examples/video/assets/clip.mp4 +0 -0
- package/examples/video/assets/clip.webm +0 -0
- package/examples/video/compositions/video-mp4.html +362 -0
- package/examples/video/compositions/video.html +362 -0
- package/examples/video/props/video.json +8 -0
- package/examples/video/scripts/make-clip.sh +109 -0
- package/examples/video/video.config.js +6 -0
- package/examples/webgl/README.md +30 -0
- package/examples/webgl/compositions/webgl.controller.js +200 -0
- package/examples/webgl/compositions/webgl.html +215 -0
- package/examples/webgl/props/webgl.json +6 -0
- package/examples/webgl/video.config.js +5 -0
- package/package.json +7 -3
- package/skills/frameyard/README.md +4 -0
- package/skills/frameyard/SKILL.md +40 -12
- package/skills/frameyard/references/audio-assets.md +25 -14
- package/skills/frameyard/references/bindings.md +73 -0
- package/skills/frameyard/references/common-mistakes.md +59 -0
- package/skills/frameyard/references/controllers.md +126 -0
- package/skills/frameyard/references/deterministic-authoring.md +143 -52
- package/skills/frameyard/references/diagnostics.md +90 -0
- package/skills/frameyard/references/failure-recovery.md +37 -14
- package/skills/frameyard/references/json-results.md +43 -9
- package/skills/frameyard/references/subtitles-karaoke.md +10 -18
- package/examples/demo/compositions/demo.js +0 -206
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<script type="application/json" data-fy="props">
|
|
6
|
+
{
|
|
7
|
+
"kicker": "Frameyard / Clip Study 001",
|
|
8
|
+
"title": "Moving",
|
|
9
|
+
"titleAccent": "Image",
|
|
10
|
+
"subtitle": "A local clip, loaded once and seeked twice from the composition clock.",
|
|
11
|
+
"detail": "Detail / second pass",
|
|
12
|
+
"accent": "#ffc37a"
|
|
13
|
+
}
|
|
14
|
+
</script>
|
|
15
|
+
<style>
|
|
16
|
+
@keyframes fade-in {
|
|
17
|
+
from {
|
|
18
|
+
opacity: 0;
|
|
19
|
+
}
|
|
20
|
+
to {
|
|
21
|
+
opacity: 1;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
@keyframes rise {
|
|
25
|
+
from {
|
|
26
|
+
opacity: 0;
|
|
27
|
+
transform: translateY(42px);
|
|
28
|
+
}
|
|
29
|
+
to {
|
|
30
|
+
opacity: 1;
|
|
31
|
+
transform: translateY(0);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
@keyframes draw {
|
|
35
|
+
from {
|
|
36
|
+
opacity: 0;
|
|
37
|
+
transform: scaleX(0);
|
|
38
|
+
}
|
|
39
|
+
to {
|
|
40
|
+
opacity: 1;
|
|
41
|
+
transform: scaleX(1);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
@keyframes outro-fade {
|
|
45
|
+
from {
|
|
46
|
+
opacity: 0;
|
|
47
|
+
}
|
|
48
|
+
to {
|
|
49
|
+
opacity: 1;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
html,
|
|
53
|
+
body,
|
|
54
|
+
fy-composition {
|
|
55
|
+
background: #04080b;
|
|
56
|
+
}
|
|
57
|
+
#scene {
|
|
58
|
+
position: relative;
|
|
59
|
+
width: 100%;
|
|
60
|
+
height: 100%;
|
|
61
|
+
overflow: hidden;
|
|
62
|
+
isolation: isolate;
|
|
63
|
+
background: #04080b;
|
|
64
|
+
color: #eef4f1;
|
|
65
|
+
font-family: Inter, "Helvetica Neue", ui-sans-serif, system-ui, sans-serif;
|
|
66
|
+
}
|
|
67
|
+
#hero {
|
|
68
|
+
position: absolute;
|
|
69
|
+
inset: 0;
|
|
70
|
+
display: block;
|
|
71
|
+
width: 100%;
|
|
72
|
+
height: 100%;
|
|
73
|
+
object-fit: cover;
|
|
74
|
+
transform: scale(calc(1 + var(--fy-progress) * 0.035));
|
|
75
|
+
transform-origin: center;
|
|
76
|
+
}
|
|
77
|
+
#scrim {
|
|
78
|
+
position: absolute;
|
|
79
|
+
inset: 0;
|
|
80
|
+
background:
|
|
81
|
+
linear-gradient(
|
|
82
|
+
90deg,
|
|
83
|
+
rgba(4, 8, 11, 0.94) 0%,
|
|
84
|
+
rgba(4, 8, 11, 0.8) 24%,
|
|
85
|
+
rgba(4, 8, 11, 0.24) 52%,
|
|
86
|
+
rgba(4, 8, 11, 0) 74%
|
|
87
|
+
),
|
|
88
|
+
linear-gradient(0deg, rgba(4, 8, 11, 0.9) 0%, rgba(4, 8, 11, 0) 34%);
|
|
89
|
+
}
|
|
90
|
+
#vignette {
|
|
91
|
+
position: absolute;
|
|
92
|
+
inset: 0;
|
|
93
|
+
background: radial-gradient(ellipse at 62% 44%, transparent 42%, rgba(0, 0, 0, 0.3) 100%);
|
|
94
|
+
pointer-events: none;
|
|
95
|
+
}
|
|
96
|
+
#frame-border {
|
|
97
|
+
position: absolute;
|
|
98
|
+
inset: 36px;
|
|
99
|
+
border: 1px solid rgba(238, 244, 241, 0.16);
|
|
100
|
+
pointer-events: none;
|
|
101
|
+
}
|
|
102
|
+
#header {
|
|
103
|
+
position: absolute;
|
|
104
|
+
top: 80px;
|
|
105
|
+
left: 148px;
|
|
106
|
+
right: 148px;
|
|
107
|
+
display: flex;
|
|
108
|
+
justify-content: space-between;
|
|
109
|
+
align-items: center;
|
|
110
|
+
gap: 32px;
|
|
111
|
+
animation: fade-in 1s linear both;
|
|
112
|
+
}
|
|
113
|
+
#kicker {
|
|
114
|
+
display: flex;
|
|
115
|
+
align-items: center;
|
|
116
|
+
gap: 15px;
|
|
117
|
+
font:
|
|
118
|
+
600 14px/1 ui-monospace,
|
|
119
|
+
monospace;
|
|
120
|
+
letter-spacing: 0.24em;
|
|
121
|
+
text-transform: uppercase;
|
|
122
|
+
color: var(--fy-prop-accent);
|
|
123
|
+
}
|
|
124
|
+
#kicker::before {
|
|
125
|
+
content: "";
|
|
126
|
+
width: 8px;
|
|
127
|
+
height: 8px;
|
|
128
|
+
background: var(--fy-prop-accent);
|
|
129
|
+
box-shadow: 0 0 16px var(--fy-prop-accent);
|
|
130
|
+
}
|
|
131
|
+
#format {
|
|
132
|
+
font:
|
|
133
|
+
500 12px/1 ui-monospace,
|
|
134
|
+
monospace;
|
|
135
|
+
letter-spacing: 0.16em;
|
|
136
|
+
text-transform: uppercase;
|
|
137
|
+
color: #8b999c;
|
|
138
|
+
}
|
|
139
|
+
#copy {
|
|
140
|
+
position: absolute;
|
|
141
|
+
top: 278px;
|
|
142
|
+
left: 148px;
|
|
143
|
+
width: 860px;
|
|
144
|
+
}
|
|
145
|
+
#title {
|
|
146
|
+
margin: 0;
|
|
147
|
+
font-size: 174px;
|
|
148
|
+
line-height: 0.84;
|
|
149
|
+
letter-spacing: -0.064em;
|
|
150
|
+
font-weight: 660;
|
|
151
|
+
}
|
|
152
|
+
#title span,
|
|
153
|
+
#title em {
|
|
154
|
+
display: block;
|
|
155
|
+
animation: rise 1s cubic-bezier(0.215, 0.61, 0.355, 1) both;
|
|
156
|
+
}
|
|
157
|
+
#title em {
|
|
158
|
+
margin-top: 0.06em;
|
|
159
|
+
font-family: "Iowan Old Style", "Palatino Linotype", Georgia, ui-serif, serif;
|
|
160
|
+
font-style: italic;
|
|
161
|
+
font-weight: 400;
|
|
162
|
+
letter-spacing: -0.035em;
|
|
163
|
+
color: var(--fy-prop-accent);
|
|
164
|
+
}
|
|
165
|
+
#subtitle {
|
|
166
|
+
width: 720px;
|
|
167
|
+
margin: 38px 0 0;
|
|
168
|
+
font-size: 29px;
|
|
169
|
+
line-height: 1.5;
|
|
170
|
+
font-weight: 420;
|
|
171
|
+
color: #a6b3b6;
|
|
172
|
+
animation: rise 1s cubic-bezier(0.215, 0.61, 0.355, 1) both;
|
|
173
|
+
}
|
|
174
|
+
#footer {
|
|
175
|
+
position: absolute;
|
|
176
|
+
left: 148px;
|
|
177
|
+
right: 148px;
|
|
178
|
+
bottom: 70px;
|
|
179
|
+
contain: layout paint style;
|
|
180
|
+
animation: fade-in 1s linear both;
|
|
181
|
+
}
|
|
182
|
+
#rule {
|
|
183
|
+
position: relative;
|
|
184
|
+
height: 1px;
|
|
185
|
+
background: rgba(238, 244, 241, 0.16);
|
|
186
|
+
transform-origin: left center;
|
|
187
|
+
animation: draw 1s cubic-bezier(0.215, 0.61, 0.355, 1) both;
|
|
188
|
+
}
|
|
189
|
+
#progress {
|
|
190
|
+
position: absolute;
|
|
191
|
+
inset: 0 auto 0 0;
|
|
192
|
+
width: calc(var(--fy-progress) * 100%);
|
|
193
|
+
background: var(--fy-prop-accent);
|
|
194
|
+
box-shadow: 0 0 14px var(--fy-prop-accent);
|
|
195
|
+
}
|
|
196
|
+
#footer-row {
|
|
197
|
+
display: flex;
|
|
198
|
+
justify-content: space-between;
|
|
199
|
+
align-items: baseline;
|
|
200
|
+
gap: 24px;
|
|
201
|
+
margin-top: 18px;
|
|
202
|
+
font:
|
|
203
|
+
500 12px/1 ui-monospace,
|
|
204
|
+
monospace;
|
|
205
|
+
letter-spacing: 0.14em;
|
|
206
|
+
color: #879598;
|
|
207
|
+
text-transform: uppercase;
|
|
208
|
+
}
|
|
209
|
+
#footer-row strong {
|
|
210
|
+
color: #edf5f2;
|
|
211
|
+
font-weight: 600;
|
|
212
|
+
}
|
|
213
|
+
#footer-row .accent {
|
|
214
|
+
color: var(--fy-prop-accent);
|
|
215
|
+
}
|
|
216
|
+
#frame-counter {
|
|
217
|
+
display: inline-block;
|
|
218
|
+
min-width: 3ch;
|
|
219
|
+
text-align: right;
|
|
220
|
+
}
|
|
221
|
+
#frame-counter::after {
|
|
222
|
+
counter-reset: fy-frame var(--fy-frame);
|
|
223
|
+
content: counter(fy-frame);
|
|
224
|
+
}
|
|
225
|
+
#detail {
|
|
226
|
+
position: absolute;
|
|
227
|
+
right: 148px;
|
|
228
|
+
bottom: 154px;
|
|
229
|
+
width: 440px;
|
|
230
|
+
}
|
|
231
|
+
#detail-bar {
|
|
232
|
+
display: flex;
|
|
233
|
+
justify-content: space-between;
|
|
234
|
+
align-items: baseline;
|
|
235
|
+
gap: 24px;
|
|
236
|
+
margin-bottom: 10px;
|
|
237
|
+
contain: layout paint style;
|
|
238
|
+
font:
|
|
239
|
+
600 11px/1 ui-monospace,
|
|
240
|
+
monospace;
|
|
241
|
+
letter-spacing: 0.18em;
|
|
242
|
+
text-transform: uppercase;
|
|
243
|
+
}
|
|
244
|
+
#detail-bar > span:first-child {
|
|
245
|
+
color: var(--fy-prop-accent);
|
|
246
|
+
}
|
|
247
|
+
#detail-time {
|
|
248
|
+
color: #98a5a6;
|
|
249
|
+
font-weight: 500;
|
|
250
|
+
}
|
|
251
|
+
#detail-window {
|
|
252
|
+
position: relative;
|
|
253
|
+
width: 440px;
|
|
254
|
+
height: 248px;
|
|
255
|
+
overflow: hidden;
|
|
256
|
+
border: 1px solid rgba(255, 195, 122, 0.65);
|
|
257
|
+
background: #000;
|
|
258
|
+
box-shadow: 0 24px 90px rgba(0, 0, 0, 0.55);
|
|
259
|
+
}
|
|
260
|
+
#detail-clip {
|
|
261
|
+
display: block;
|
|
262
|
+
width: 100%;
|
|
263
|
+
height: 100%;
|
|
264
|
+
object-fit: cover;
|
|
265
|
+
object-position: 68% 50%;
|
|
266
|
+
transform: scale(1.3);
|
|
267
|
+
}
|
|
268
|
+
#detail-tint {
|
|
269
|
+
position: absolute;
|
|
270
|
+
inset: 0;
|
|
271
|
+
background: linear-gradient(0deg, rgba(4, 8, 11, 0.28) 0%, rgba(4, 8, 11, 0) 45%);
|
|
272
|
+
pointer-events: none;
|
|
273
|
+
}
|
|
274
|
+
#outro-fade {
|
|
275
|
+
position: absolute;
|
|
276
|
+
inset: 0;
|
|
277
|
+
background: #04080b;
|
|
278
|
+
pointer-events: none;
|
|
279
|
+
opacity: 0;
|
|
280
|
+
animation: outro-fade 1s linear both;
|
|
281
|
+
}
|
|
282
|
+
</style>
|
|
283
|
+
</head>
|
|
284
|
+
<body>
|
|
285
|
+
<fy-composition width="1920" height="1080" fps="30" duration-in-frames="360">
|
|
286
|
+
<div id="scene">
|
|
287
|
+
<video
|
|
288
|
+
id="hero"
|
|
289
|
+
src="assets/clip.webm"
|
|
290
|
+
data-fy-start="0"
|
|
291
|
+
data-fy-trim="0"
|
|
292
|
+
data-fy-duration="12"
|
|
293
|
+
data-fy-volume="0.85"
|
|
294
|
+
></video>
|
|
295
|
+
<div id="scrim"></div>
|
|
296
|
+
<div id="vignette"></div>
|
|
297
|
+
<div id="frame-border"></div>
|
|
298
|
+
|
|
299
|
+
<header id="header" data-fy-anim-start="8" data-fy-anim-duration="28">
|
|
300
|
+
<div id="kicker" data-fy-text="kicker"></div>
|
|
301
|
+
<div id="format">Embedded video / VP9 + Opus / 30 FPS</div>
|
|
302
|
+
</header>
|
|
303
|
+
|
|
304
|
+
<div id="copy">
|
|
305
|
+
<h1 id="title">
|
|
306
|
+
<span
|
|
307
|
+
data-fy-text="title"
|
|
308
|
+
data-fy-anim-start="14"
|
|
309
|
+
data-fy-anim-duration="44"
|
|
310
|
+
data-fy-anim-easing="cubic-bezier(0.215, 0.61, 0.355, 1)"
|
|
311
|
+
></span>
|
|
312
|
+
<em
|
|
313
|
+
data-fy-text="titleAccent"
|
|
314
|
+
data-fy-anim-start="26"
|
|
315
|
+
data-fy-anim-duration="44"
|
|
316
|
+
data-fy-anim-easing="cubic-bezier(0.215, 0.61, 0.355, 1)"
|
|
317
|
+
></em>
|
|
318
|
+
</h1>
|
|
319
|
+
<p
|
|
320
|
+
id="subtitle"
|
|
321
|
+
data-fy-text="subtitle"
|
|
322
|
+
data-fy-anim-start="40"
|
|
323
|
+
data-fy-anim-duration="40"
|
|
324
|
+
data-fy-anim-easing="cubic-bezier(0.215, 0.61, 0.355, 1)"
|
|
325
|
+
></p>
|
|
326
|
+
</div>
|
|
327
|
+
|
|
328
|
+
<div id="detail">
|
|
329
|
+
<div id="detail-bar">
|
|
330
|
+
<span data-fy-text="detail"></span>
|
|
331
|
+
<span id="detail-time">SRC 5.8–10.8 S</span>
|
|
332
|
+
</div>
|
|
333
|
+
<div id="detail-window">
|
|
334
|
+
<video
|
|
335
|
+
id="detail-clip"
|
|
336
|
+
src="assets/clip.webm"
|
|
337
|
+
data-fy-start="120"
|
|
338
|
+
data-fy-trim="5.8"
|
|
339
|
+
data-fy-duration="5"
|
|
340
|
+
data-fy-audio="off"
|
|
341
|
+
></video>
|
|
342
|
+
<div id="detail-tint"></div>
|
|
343
|
+
</div>
|
|
344
|
+
</div>
|
|
345
|
+
|
|
346
|
+
<div id="footer" data-fy-anim-start="46" data-fy-anim-duration="30">
|
|
347
|
+
<div id="rule" data-fy-anim-start="52" data-fy-anim-duration="50">
|
|
348
|
+
<div id="progress"></div>
|
|
349
|
+
</div>
|
|
350
|
+
<div id="footer-row">
|
|
351
|
+
<span>Hero / SRC <strong>0.0–12.0 S</strong></span>
|
|
352
|
+
<span>Frame <strong id="frame-counter"></strong> / 359</span>
|
|
353
|
+
<span class="accent">Frame-seeked</span>
|
|
354
|
+
<span>12.00 S · 30 FPS</span>
|
|
355
|
+
</div>
|
|
356
|
+
</div>
|
|
357
|
+
|
|
358
|
+
<div id="outro-fade" data-fy-anim-start="329" data-fy-anim-duration="30"></div>
|
|
359
|
+
</div>
|
|
360
|
+
</fy-composition>
|
|
361
|
+
</body>
|
|
362
|
+
</html>
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# Regenerate examples/video/assets/clip.webm and clip.mp4.
|
|
4
|
+
#
|
|
5
|
+
# The visual is a procedural gradient field warped by two sinusoidal
|
|
6
|
+
# displacement maps. The audio is an additive sine pad with pink-noise air.
|
|
7
|
+
# Fixed seeds keep the generated grain stable across runs on the same FFmpeg.
|
|
8
|
+
#
|
|
9
|
+
# Requires FFmpeg with libvpx-vp9, libx264, Opus, and AAC support.
|
|
10
|
+
#
|
|
11
|
+
# Usage:
|
|
12
|
+
# examples/video/scripts/make-clip.sh
|
|
13
|
+
# examples/video/scripts/make-clip.sh --out-dir /tmp/frameyard-clip
|
|
14
|
+
|
|
15
|
+
set -euo pipefail
|
|
16
|
+
|
|
17
|
+
usage() {
|
|
18
|
+
cat <<'EOF'
|
|
19
|
+
Usage: make-clip.sh [--out-dir <directory>]
|
|
20
|
+
|
|
21
|
+
Writes clip.webm (VP9 + Opus) and clip.mp4 (H.264 + AAC) to
|
|
22
|
+
examples/video/assets by default.
|
|
23
|
+
EOF
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
|
27
|
+
out_dir="$(cd -- "$script_dir/.." && pwd)/assets"
|
|
28
|
+
|
|
29
|
+
while [ "$#" -gt 0 ]; do
|
|
30
|
+
case "$1" in
|
|
31
|
+
--out-dir)
|
|
32
|
+
if [ "$#" -lt 2 ]; then
|
|
33
|
+
echo "make-clip: --out-dir requires a directory." >&2
|
|
34
|
+
usage >&2
|
|
35
|
+
exit 2
|
|
36
|
+
fi
|
|
37
|
+
out_dir="$2"
|
|
38
|
+
shift 2
|
|
39
|
+
;;
|
|
40
|
+
-h | --help)
|
|
41
|
+
usage
|
|
42
|
+
exit 0
|
|
43
|
+
;;
|
|
44
|
+
*)
|
|
45
|
+
echo "make-clip: unknown argument: $1" >&2
|
|
46
|
+
usage >&2
|
|
47
|
+
exit 2
|
|
48
|
+
;;
|
|
49
|
+
esac
|
|
50
|
+
done
|
|
51
|
+
|
|
52
|
+
for tool in ffmpeg ffprobe; do
|
|
53
|
+
if ! command -v "$tool" >/dev/null 2>&1; then
|
|
54
|
+
echo "make-clip: $tool is required on PATH." >&2
|
|
55
|
+
exit 1
|
|
56
|
+
fi
|
|
57
|
+
done
|
|
58
|
+
|
|
59
|
+
mkdir -p "$out_dir"
|
|
60
|
+
out_dir="$(cd -- "$out_dir" && pwd)"
|
|
61
|
+
work_dir="$(mktemp -d "$out_dir/.make-clip.XXXXXX")"
|
|
62
|
+
trap 'rm -rf "$work_dir"' EXIT
|
|
63
|
+
|
|
64
|
+
# 12 seconds of a slow circular gradient, warped by two radial sine/cosine
|
|
65
|
+
# displacement maps. Scaling up and cropping hides the displacement edges.
|
|
66
|
+
ffmpeg -hide_banner -loglevel error -y \
|
|
67
|
+
-f lavfi -i "gradients=s=1280x720:r=30:d=12:c0=0x071827:c1=0x0d3a3a:c2=0x2f8a7e:c3=0xffd7a0:c4=0x123a58:c5=0x8fe6c8:n=6:type=circular:speed=0.02:seed=9" \
|
|
68
|
+
-f lavfi -i "nullsrc=s=1280x720:r=30:d=12,geq=lum='128+72*sin(hypot(X-640,Y-360)/42-T*0.55)':cb=128:cr=128" \
|
|
69
|
+
-f lavfi -i "nullsrc=s=1280x720:r=30:d=12,geq=lum='128+68*cos(hypot(X-640,Y-360)/55+T*0.47)':cb=128:cr=128" \
|
|
70
|
+
-filter_complex "[0:v][1:v][2:v]displace=edge=smear,scale=1440:810,crop=1280:720,gblur=sigma=6,eq=contrast=1.26:brightness=0.015:saturation=1.06,noise=alls=4:allf=t+u:all_seed=7,vignette=PI/3.6,format=yuv420p[v]" \
|
|
71
|
+
-map "[v]" -t 12 -an \
|
|
72
|
+
-c:v libvpx-vp9 -crf 31 -b:v 0 -row-mt 1 -tile-columns 2 -cpu-used 2 -g 60 \
|
|
73
|
+
"$work_dir/clip-video.webm"
|
|
74
|
+
|
|
75
|
+
# A slow A-minor-ish pad with movement from chorus/echo and a filtered
|
|
76
|
+
# pink-noise bed. The clip fades in and out so the timeline edit is soft.
|
|
77
|
+
ffmpeg -hide_banner -loglevel error -y \
|
|
78
|
+
-f lavfi -i "aevalsrc=exprs='0.24*sin(2*PI*110*t)+0.12*sin(2*PI*164.81*t)+0.10*sin(2*PI*220*t)*(0.8+0.2*sin(2*PI*0.07*t))+0.06*sin(2*PI*261.63*t)+0.04*sin(2*PI*329.63*t)+0.018*sin(2*PI*440*t)':s=48000:d=12" \
|
|
79
|
+
-f lavfi -i "anoisesrc=color=pink:sample_rate=48000:duration=12:amplitude=0.08:seed=7" \
|
|
80
|
+
-filter_complex "[0:a]chorus=0.6:0.9:50|60:0.4|0.32:0.25|0.3:2|1.3,aecho=0.8:0.6:280|520:0.25|0.16,lowpass=f=4200,volume=0.9[pad];[1:a]lowpass=f=700,volume=0.075[air];[pad][air]amix=inputs=2:duration=longest:normalize=0,afade=t=in:st=0:d=2,afade=t=out:st=9.4:d=2.6,alimiter=limit=0.9,pan=stereo|c0=c0|c1=c0[a]" \
|
|
81
|
+
-map "[a]" -t 12 -c:a libopus -b:a 96k \
|
|
82
|
+
"$work_dir/clip-audio.webm"
|
|
83
|
+
|
|
84
|
+
ffmpeg -hide_banner -loglevel error -y \
|
|
85
|
+
-i "$work_dir/clip-video.webm" \
|
|
86
|
+
-i "$work_dir/clip-audio.webm" \
|
|
87
|
+
-map 0:v:0 -map 1:a:0 -c copy \
|
|
88
|
+
"$work_dir/clip.webm"
|
|
89
|
+
|
|
90
|
+
# The MP4 variant is a transcode of the WebM source with the same timeline.
|
|
91
|
+
ffmpeg -hide_banner -loglevel error -y \
|
|
92
|
+
-i "$work_dir/clip.webm" \
|
|
93
|
+
-c:v libx264 -preset medium -crf 20 -pix_fmt yuv420p -g 60 \
|
|
94
|
+
-c:a aac -b:a 128k -movflags +faststart \
|
|
95
|
+
"$work_dir/clip.mp4"
|
|
96
|
+
|
|
97
|
+
for file in "$work_dir/clip.webm" "$work_dir/clip.mp4"; do
|
|
98
|
+
ffprobe -v error -select_streams v:0 \
|
|
99
|
+
-show_entries stream=codec_name,width,height,r_frame_rate \
|
|
100
|
+
-of default=noprint_wrappers=1 "$file" >/dev/null
|
|
101
|
+
ffprobe -v error -select_streams a:0 -show_entries stream=codec_name \
|
|
102
|
+
-of default=noprint_wrappers=1 "$file" >/dev/null
|
|
103
|
+
done
|
|
104
|
+
|
|
105
|
+
mv -f "$work_dir/clip.webm" "$out_dir/clip.webm"
|
|
106
|
+
mv -f "$work_dir/clip.mp4" "$out_dir/clip.mp4"
|
|
107
|
+
|
|
108
|
+
echo "Wrote $out_dir/clip.webm (VP9 + Opus)"
|
|
109
|
+
echo "Wrote $out_dir/clip.mp4 (H.264 + AAC)"
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Frameyard WebGL example
|
|
2
|
+
|
|
3
|
+
A 12-second, 1920×1080 composition that renders a procedural light field with a
|
|
4
|
+
WebGL fragment shader and layers HTML/CSS typography over it:
|
|
5
|
+
|
|
6
|
+
- a WebGL 1 fullscreen triangle with one cached program and cached uniforms
|
|
7
|
+
- shader motion derived only from the composition frame
|
|
8
|
+
- props for the kicker, title, subtitle, and accent color
|
|
9
|
+
- frame-scheduled CSS typography and a frame-state progress bar
|
|
10
|
+
- controller-updated frame and time readouts
|
|
11
|
+
|
|
12
|
+
The composition lives in `compositions/webgl.html`; the external controller is
|
|
13
|
+
`compositions/webgl.controller.js`. Draw every frame from `update()` and do not
|
|
14
|
+
rely on the previous drawing buffer. Production sessions pin software WebGL so
|
|
15
|
+
the captured pixels do not depend on the host GPU.
|
|
16
|
+
|
|
17
|
+
From the repository root:
|
|
18
|
+
|
|
19
|
+
```sh
|
|
20
|
+
pnpm build
|
|
21
|
+
node dist/cli.js check webgl --project examples/webgl
|
|
22
|
+
node dist/cli.js still webgl --frame 210 --project examples/webgl --out webgl-still.png
|
|
23
|
+
node dist/cli.js render webgl --project examples/webgl --props examples/webgl/props/webgl.json
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Start the interactive preview with:
|
|
27
|
+
|
|
28
|
+
```sh
|
|
29
|
+
node dist/cli.js dev --project examples/webgl
|
|
30
|
+
```
|