multiplane 0.0.0-stage → 0.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/LICENSE +202 -0
- package/README.en.md +322 -0
- package/README.md +321 -2
- package/THIRD_PARTY_NOTICES.md +30 -0
- package/bin/multiplane +98 -0
- package/examples/alpha.html +18 -0
- package/examples/animation.html +17 -0
- package/examples/assets/OFL.txt +93 -0
- package/examples/assets/Roboto-Variable.ttf +0 -0
- package/examples/assets/card.svg +6 -0
- package/examples/assets/readme-card-0.svg +1 -0
- package/examples/assets/readme-card-1.svg +1 -0
- package/examples/assets/readme-card-2.svg +1 -0
- package/examples/assets/readme-detail.svg +1 -0
- package/examples/readme-cards.html +23 -0
- package/examples/readme-cut.html +13 -0
- package/examples/readme-depth.html +15 -0
- package/examples/readme-wheel.html +14 -0
- package/examples/three-cards.html +17 -0
- package/examples/typography.html +17 -0
- package/licenses/GPL-3.0.txt +232 -0
- package/licenses/LGPL-3.0.txt +304 -0
- package/licenses/acorn-LICENSE +21 -0
- package/licenses/colour-LICENSE.md +82 -0
- package/licenses/detect-libc-LICENSE +201 -0
- package/licenses/entities-LICENSE +11 -0
- package/licenses/nanoid-LICENSE +20 -0
- package/licenses/native/README.ijg +258 -0
- package/licenses/native/aom-LICENSE +27 -0
- package/licenses/native/aom-PATENTS +108 -0
- package/licenses/native/archive-COPYING +65 -0
- package/licenses/native/cairo-COPYING +33 -0
- package/licenses/native/cairo-COPYING-MPL-1.1 +470 -0
- package/licenses/native/cgif-LICENSE +21 -0
- package/licenses/native/exif-COPYING +504 -0
- package/licenses/native/expat-COPYING +21 -0
- package/licenses/native/ffi-LICENSE +21 -0
- package/licenses/native/fontconfig-COPYING +200 -0
- package/licenses/native/freetype-FTL.TXT +169 -0
- package/licenses/native/fribidi-COPYING +501 -0
- package/licenses/native/glib-COPYING +175 -0
- package/licenses/native/harfbuzz-COPYING +42 -0
- package/licenses/native/heif-COPYING +871 -0
- package/licenses/native/highway-LICENSE +371 -0
- package/licenses/native/imagequant-COPYRIGHT +36 -0
- package/licenses/native/lcms-COPYING +22 -0
- package/licenses/native/mozjpeg-LICENSE.md +135 -0
- package/licenses/native/nsgif-COPYING +21 -0
- package/licenses/native/pango-COPYING +482 -0
- package/licenses/native/pixman-COPYING +42 -0
- package/licenses/native/png-LICENSE +134 -0
- package/licenses/native/proxy-libintl-COPYING +482 -0
- package/licenses/native/rsvg-COPYING.LIB +502 -0
- package/licenses/native/sources.json +240 -0
- package/licenses/native/tiff-LICENSE.md +49 -0
- package/licenses/native/uhdr-LICENSE +230 -0
- package/licenses/native/vips-COPYING +502 -0
- package/licenses/native/webp-COPYING +30 -0
- package/licenses/native/webp-PATENTS +23 -0
- package/licenses/native/xml2-Copyright +24 -0
- package/licenses/native/zlib-ng-LICENSE.md +19 -0
- package/licenses/parse5-LICENSE +19 -0
- package/licenses/picocolors-LICENSE +15 -0
- package/licenses/playwright-LICENSE +202 -0
- package/licenses/playwright-NOTICE +5 -0
- package/licenses/playwright-ThirdPartyNotices.txt +14 -0
- package/licenses/playwright-core-ThirdPartyNotices.txt +13 -0
- package/licenses/postcss-LICENSE +20 -0
- package/licenses/postcss-value-parser-LICENSE +22 -0
- package/licenses/semver-LICENSE +15 -0
- package/licenses/sharp-LICENSE +191 -0
- package/licenses/sharp-win32-x64-LICENSE +191 -0
- package/licenses/sharp-win32-x64-NOTICES.md +59 -0
- package/licenses/source-map-js-LICENSE +28 -0
- package/licenses/three-LICENSE +21 -0
- package/package.json +92 -6
- package/skills/multiplane/SKILL.md +213 -0
- package/src/alpha.mjs +69 -0
- package/src/artifacts.mjs +61 -0
- package/src/benchmark.mjs +140 -0
- package/src/browser-recovery.mjs +26 -0
- package/src/capture.mjs +224 -0
- package/src/config.mjs +79 -0
- package/src/doctor.mjs +56 -0
- package/src/draft.mjs +30 -0
- package/src/encoder.mjs +16 -0
- package/src/environment.mjs +56 -0
- package/src/errors.mjs +51 -0
- package/src/events.mjs +252 -0
- package/src/ffmpeg.mjs +32 -0
- package/src/format.mjs +27 -0
- package/src/frame-rate.mjs +28 -0
- package/src/home-migration.mjs +54 -0
- package/src/hosted.mjs +134 -0
- package/src/index.mjs +14 -0
- package/src/inspect.mjs +20 -0
- package/src/jobs.mjs +51 -0
- package/src/lint.mjs +248 -0
- package/src/output-alpha.mjs +36 -0
- package/src/output-check.mjs +110 -0
- package/src/pixel-comparison.mjs +43 -0
- package/src/platform.mjs +114 -0
- package/src/png-native.mjs +12 -0
- package/src/png.mjs +74 -0
- package/src/post.mjs +433 -0
- package/src/preview.mjs +23 -0
- package/src/process-identity.mjs +17 -0
- package/src/process.mjs +9 -0
- package/src/render-pool.mjs +93 -0
- package/src/render-worker.mjs +59 -0
- package/src/render.mjs +157 -0
- package/src/resource-path.mjs +29 -0
- package/src/resources.mjs +27 -0
- package/src/runtime/camera.js +37 -0
- package/src/runtime/shot.css +9 -0
- package/src/runtime/shot.js +71 -0
- package/src/serve.mjs +23 -0
- package/src/server.mjs +93 -0
- package/src/setup.mjs +51 -0
- package/src/skills.mjs +128 -0
- package/src/static-reuse.mjs +265 -0
- package/src/stills.mjs +81 -0
- package/src/three/index.js +348 -0
- package/src/user-home.mjs +105 -0
- package/src/verify.mjs +161 -0
- package/src/video-pipe.mjs +6 -0
- package/src/workers.mjs +62 -0
- package/tools/bench/shots/canvas-2d.html +1 -0
- package/tools/bench/shots/dom-light.html +1 -0
- package/tools/bench/shots/scene.js +45 -0
- package/tools/bench/shots/webgl.html +1 -0
- package/tools/install-browser.mjs +18 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
|
|
2
|
+
Apache License
|
|
3
|
+
Version 2.0, January 2004
|
|
4
|
+
http://www.apache.org/licenses/
|
|
5
|
+
|
|
6
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
7
|
+
|
|
8
|
+
1. Definitions.
|
|
9
|
+
|
|
10
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
11
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
12
|
+
|
|
13
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
14
|
+
the copyright owner that is granting the License.
|
|
15
|
+
|
|
16
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
17
|
+
other entities that control, are controlled by, or are under common
|
|
18
|
+
control with that entity. For the purposes of this definition,
|
|
19
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
20
|
+
direction or management of such entity, whether by contract or
|
|
21
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
22
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
23
|
+
|
|
24
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
25
|
+
exercising permissions granted by this License.
|
|
26
|
+
|
|
27
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
28
|
+
including but not limited to software source code, documentation
|
|
29
|
+
source, and configuration files.
|
|
30
|
+
|
|
31
|
+
"Object" form shall mean any form resulting from mechanical
|
|
32
|
+
transformation or translation of a Source form, including but
|
|
33
|
+
not limited to compiled object code, generated documentation,
|
|
34
|
+
and conversions to other media types.
|
|
35
|
+
|
|
36
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
37
|
+
Object form, made available under the License, as indicated by a
|
|
38
|
+
copyright notice that is included in or attached to the work
|
|
39
|
+
(an example is provided in the Appendix below).
|
|
40
|
+
|
|
41
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
42
|
+
form, that is based on (or derived from) the Work and for which the
|
|
43
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
44
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
45
|
+
of this License, Derivative Works shall not include works that remain
|
|
46
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
47
|
+
the Work and Derivative Works thereof.
|
|
48
|
+
|
|
49
|
+
"Contribution" shall mean any work of authorship, including
|
|
50
|
+
the original version of the Work and any modifications or additions
|
|
51
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
52
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
53
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
54
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
55
|
+
means any form of electronic, verbal, or written communication sent
|
|
56
|
+
to the Licensor or its representatives, including but not limited to
|
|
57
|
+
communication on electronic mailing lists, source code control systems,
|
|
58
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
59
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
60
|
+
excluding communication that is conspicuously marked or otherwise
|
|
61
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
62
|
+
|
|
63
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
64
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
65
|
+
subsequently incorporated within the Work.
|
|
66
|
+
|
|
67
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
68
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
69
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
70
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
71
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
72
|
+
Work and such Derivative Works in Source or Object form.
|
|
73
|
+
|
|
74
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
75
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
76
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
77
|
+
(except as stated in this section) patent license to make, have made,
|
|
78
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
79
|
+
where such license applies only to those patent claims licensable
|
|
80
|
+
by such Contributor that are necessarily infringed by their
|
|
81
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
82
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
83
|
+
institute patent litigation against any entity (including a
|
|
84
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
85
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
86
|
+
or contributory patent infringement, then any patent licenses
|
|
87
|
+
granted to You under this License for that Work shall terminate
|
|
88
|
+
as of the date such litigation is filed.
|
|
89
|
+
|
|
90
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
91
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
92
|
+
modifications, and in Source or Object form, provided that You
|
|
93
|
+
meet the following conditions:
|
|
94
|
+
|
|
95
|
+
(a) You must give any other recipients of the Work or
|
|
96
|
+
Derivative Works a copy of this License; and
|
|
97
|
+
|
|
98
|
+
(b) You must cause any modified files to carry prominent notices
|
|
99
|
+
stating that You changed the files; and
|
|
100
|
+
|
|
101
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
102
|
+
that You distribute, all copyright, patent, trademark, and
|
|
103
|
+
attribution notices from the Source form of the Work,
|
|
104
|
+
excluding those notices that do not pertain to any part of
|
|
105
|
+
the Derivative Works; and
|
|
106
|
+
|
|
107
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
108
|
+
distribution, then any Derivative Works that You distribute must
|
|
109
|
+
include a readable copy of the attribution notices contained
|
|
110
|
+
within such NOTICE file, excluding those notices that do not
|
|
111
|
+
pertain to any part of the Derivative Works, in at least one
|
|
112
|
+
of the following places: within a NOTICE text file distributed
|
|
113
|
+
as part of the Derivative Works; within the Source form or
|
|
114
|
+
documentation, if provided along with the Derivative Works; or,
|
|
115
|
+
within a display generated by the Derivative Works, if and
|
|
116
|
+
wherever such third-party notices normally appear. The contents
|
|
117
|
+
of the NOTICE file are for informational purposes only and
|
|
118
|
+
do not modify the License. You may add Your own attribution
|
|
119
|
+
notices within Derivative Works that You distribute, alongside
|
|
120
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
121
|
+
that such additional attribution notices cannot be construed
|
|
122
|
+
as modifying the License.
|
|
123
|
+
|
|
124
|
+
You may add Your own copyright statement to Your modifications and
|
|
125
|
+
may provide additional or different license terms and conditions
|
|
126
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
127
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
128
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
129
|
+
the conditions stated in this License.
|
|
130
|
+
|
|
131
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
132
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
133
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
134
|
+
this License, without any additional terms or conditions.
|
|
135
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
136
|
+
the terms of any separate license agreement you may have executed
|
|
137
|
+
with Licensor regarding such Contributions.
|
|
138
|
+
|
|
139
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
140
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
141
|
+
except as required for reasonable and customary use in describing the
|
|
142
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
143
|
+
|
|
144
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
145
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
146
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
147
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
148
|
+
implied, including, without limitation, any warranties or conditions
|
|
149
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
150
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
151
|
+
appropriateness of using or redistributing the Work and assume any
|
|
152
|
+
risks associated with Your exercise of permissions under this License.
|
|
153
|
+
|
|
154
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
155
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
156
|
+
unless required by applicable law (such as deliberate and grossly
|
|
157
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
158
|
+
liable to You for damages, including any direct, indirect, special,
|
|
159
|
+
incidental, or consequential damages of any character arising as a
|
|
160
|
+
result of this License or out of the use or inability to use the
|
|
161
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
162
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
163
|
+
other commercial damages or losses), even if such Contributor
|
|
164
|
+
has been advised of the possibility of such damages.
|
|
165
|
+
|
|
166
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
167
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
168
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
169
|
+
or other liability obligations and/or rights consistent with this
|
|
170
|
+
License. However, in accepting such obligations, You may act only
|
|
171
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
172
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
173
|
+
defend, and hold each Contributor harmless for any liability
|
|
174
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
175
|
+
of your accepting any such warranty or additional liability.
|
|
176
|
+
|
|
177
|
+
END OF TERMS AND CONDITIONS
|
|
178
|
+
|
|
179
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
180
|
+
|
|
181
|
+
To apply the Apache License to your work, attach the following
|
|
182
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
183
|
+
replaced with your own identifying information. (Don't include
|
|
184
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
185
|
+
comment syntax for the file format. We also recommend that a
|
|
186
|
+
file or class name and description of purpose be included on the
|
|
187
|
+
same "printed page" as the copyright notice for easier
|
|
188
|
+
identification within third-party archives.
|
|
189
|
+
|
|
190
|
+
Copyright [yyyy] [name of copyright owner]
|
|
191
|
+
|
|
192
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
193
|
+
you may not use this file except in compliance with the License.
|
|
194
|
+
You may obtain a copy of the License at
|
|
195
|
+
|
|
196
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
197
|
+
|
|
198
|
+
Unless required by applicable law or agreed to in writing, software
|
|
199
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
200
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
201
|
+
See the License for the specific language governing permissions and
|
|
202
|
+
limitations under the License.
|
package/README.en.md
ADDED
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
<picture>
|
|
2
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/LBEILC/Multiplane/main/app/design/icons/readme-dark.svg">
|
|
3
|
+
<img src="https://raw.githubusercontent.com/LBEILC/Multiplane/main/app/design/icons/readme-light.svg" width="72" height="72" alt="">
|
|
4
|
+
</picture>
|
|
5
|
+
|
|
6
|
+
# Multiplane
|
|
7
|
+
|
|
8
|
+
English · [简体中文](README.md)
|
|
9
|
+
|
|
10
|
+
A local, headless HTML video renderer for AI agents. **A shot is an ordinary web page**: the page implements `render(t)`, and the image depends only on time `t`. Multiplane captures frames with headless Chromium, applies post-processing such as supersampling, motion blur, and film grain, encodes video with FFmpeg, and checks whether a shot is deterministic.
|
|
11
|
+
|
|
12
|
+
<img src="https://raw.githubusercontent.com/LBEILC/Multiplane/main/docs/images/hero.webp" width="720" alt="3D cards rendered by Multiplane, with depth of field and motion blur">
|
|
13
|
+
|
|
14
|
+
> Version `0.3.0`. The full test suite passes in CI on Windows, macOS and Linux; Windows has also been verified on a real machine. Interfaces may still change before 1.0.
|
|
15
|
+
|
|
16
|
+
The name comes from Disney's multiplane camera: flat artwork placed at different depths, photographed frame by frame with real depth of field. Here, this corresponds to layered web content, a 3D card layer, and accumulated depth of field.
|
|
17
|
+
|
|
18
|
+
## Features
|
|
19
|
+
|
|
20
|
+
- **No new framework to learn**. Shots use HTML, CSS, and JavaScript, which agents are well suited to write.
|
|
21
|
+
- **Lint before rendering**. `lint` runs without a browser in under a second. It flags wall-clock time, random numbers, CSS animations, remote resources, and other patterns that break determinism, with file, line, and column locations. Rendering commands run it automatically before starting.
|
|
22
|
+
- **Verifiable**. `verify` renders the same frames out of order, after page reloads, and with single and multiple processes, then compares them pixel by pixel. Mismatches include the affected times, regions, and diff images.
|
|
23
|
+
- **Motion blur respects hard cuts**. Each frame uses multiple time samples within the shutter interval, blended in linear light. Images on opposite sides of the shot's declared hard cut times, `cuts`, are never blurred together.
|
|
24
|
+
- **3D layer and accumulated depth of field** (optional, `multiplane/three`): place image cards in 3D space and accumulate multiple samples for real out-of-focus blur, combined with motion blur.
|
|
25
|
+
- **Machine-readable**. All commands support `--json` and emit an event stream with stable error codes. Each run has its own job directory, supports progress queries, and can be cancelled.
|
|
26
|
+
- **Stills too**. `stills` writes PNG frames at the times you choose, with the same supersampling, motion blur, depth of field and alpha as video. Use it to check frames, or to produce final images such as covers, posters and illustrations.
|
|
27
|
+
- **Deterministic**. The renderer itself introduces no wall-clock time or unseeded random numbers. The same input in the same environment produces identical pixels, backed by tests.
|
|
28
|
+
|
|
29
|
+
## What it looks like
|
|
30
|
+
|
|
31
|
+
**Motion blur.** Left: no motion blur. Middle: a fixed 8 samples, so fast spinning spokes break into separate copies. Right: samples added automatically according to how far things move, giving a smooth streak. The bottom row is a close-up.
|
|
32
|
+
|
|
33
|
+
<img src="https://raw.githubusercontent.com/LBEILC/Multiplane/main/docs/images/motion-blur.png" width="800" alt="Motion blur: none, fixed 8 samples, samples added automatically">
|
|
34
|
+
|
|
35
|
+
**Cuts stay clean.** The first frame after a cut. Left: the cut is not declared, and motion blur mixes in the colour and the circle from the shot before it. Right: the shot declares `cuts`, and the cut is clean.
|
|
36
|
+
|
|
37
|
+
<img src="https://raw.githubusercontent.com/LBEILC/Multiplane/main/docs/images/hard-cut.png" width="800" alt="Cut: not declared and declared with cuts">
|
|
38
|
+
|
|
39
|
+
**3D depth of field.** The same cards, focused on the foreground and on the background. The defocus is accumulated from many samples rather than blurred afterwards from a depth map, so foreground edges do not bleed and the background does not leak into foreground outlines. The bottom row is a close-up of the foreground card.
|
|
40
|
+
|
|
41
|
+
<img src="https://raw.githubusercontent.com/LBEILC/Multiplane/main/docs/images/depth-of-field.png" width="800" alt="Depth of field: focused on the foreground and on the background">
|
|
42
|
+
|
|
43
|
+
## Quick start
|
|
44
|
+
|
|
45
|
+
Requires Node.js 24 or later and FFmpeg with libx264 support.
|
|
46
|
+
|
|
47
|
+
```sh
|
|
48
|
+
npm install multiplane
|
|
49
|
+
npx playwright install chromium
|
|
50
|
+
npx multiplane doctor
|
|
51
|
+
npx multiplane render node_modules/multiplane/examples/animation.html
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Recent npm versions do not run install scripts of dependencies, so the second command downloads the Chromium build that Playwright uses. To work from a clone of this repository instead, run `npm install` in it and render `examples/animation.html` directly.
|
|
55
|
+
|
|
56
|
+
Give your agent the usage skill with `npx multiplane skill install --host all` (Claude Code: `~/.claude/skills/multiplane`, Codex: `~/.agents/skills/multiplane`), or let the desktop app install it. Start a new agent session afterwards.
|
|
57
|
+
|
|
58
|
+
In a clone of this repository, `npm install` downloads that Chromium build itself, reusing it if already installed; in an offline environment, set `PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1` to skip the download, then run `npx playwright install chromium` later. On Linux, Chromium also needs system libraries: run `npx playwright install-deps chromium` once (`doctor` tells you when they are missing).
|
|
59
|
+
|
|
60
|
+
### FFmpeg
|
|
61
|
+
|
|
62
|
+
Multiplane does not bundle FFmpeg. Install one that supports libx264:
|
|
63
|
+
|
|
64
|
+
| System | One way to get it |
|
|
65
|
+
|---|---|
|
|
66
|
+
| Windows | a build from the [official FFmpeg download page](https://ffmpeg.org/download.html) |
|
|
67
|
+
| macOS | `brew install ffmpeg` |
|
|
68
|
+
| Linux | your distribution's package, for example `sudo apt-get install ffmpeg` |
|
|
69
|
+
|
|
70
|
+
The licence of FFmpeg depends on the build you install. Then tell Multiplane where to find it using one of these methods, in priority order:
|
|
71
|
+
|
|
72
|
+
1. The command-line option `--ffmpeg <path>`;
|
|
73
|
+
2. The environment variable `MULTIPLANE_FFMPEG`;
|
|
74
|
+
3. `ffmpeg` in the user config file (see "User config and job index" below);
|
|
75
|
+
4. The system PATH.
|
|
76
|
+
|
|
77
|
+
To save the path once for every project, run `npx multiplane config set ffmpeg <path>`; it also checks the file and reports whether it is usable.
|
|
78
|
+
|
|
79
|
+
The default is software encoding with `x264`. With an NVIDIA GPU, use `--encoder nvenc-h264` or `--encoder nvenc-hevc`. `doctor` checks whether FFmpeg can run and supports the required encoders.
|
|
80
|
+
|
|
81
|
+
## Write a shot
|
|
82
|
+
|
|
83
|
+
```html
|
|
84
|
+
<!doctype html>
|
|
85
|
+
<html lang="en"><meta charset="utf-8"><title>Example</title>
|
|
86
|
+
<link rel="stylesheet" href="/multiplane/runtime/shot.css">
|
|
87
|
+
<style>
|
|
88
|
+
#stage { width: 1920px; height: 1080px; background: #111; }
|
|
89
|
+
#box { position: absolute; left: 0; top: 490px; width: 100px; height: 100px; background: #eee; }
|
|
90
|
+
</style>
|
|
91
|
+
<div id="stage"><div id="box"></div></div>
|
|
92
|
+
<script src="/multiplane/runtime/shot.js"></script>
|
|
93
|
+
<script>
|
|
94
|
+
const box = document.querySelector('#box');
|
|
95
|
+
Shot.define({
|
|
96
|
+
duration: 4,
|
|
97
|
+
fps: 30,
|
|
98
|
+
render(t) {
|
|
99
|
+
box.style.transform = `translateX(${Math.min(t / 3, 1) * 1820}px)`;
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
</script></html>
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
There are only three requirements:
|
|
106
|
+
|
|
107
|
+
- Draw everything inside `#stage`, with fixed CSS width and height defining the frame dimensions;
|
|
108
|
+
- Make `render(t)` synchronous. The same `t` must always produce the same image;
|
|
109
|
+
- Use local files for fonts, images, and other assets, and finish loading them in `setup()` / `ready`.
|
|
110
|
+
|
|
111
|
+
For the full contract, patterns that break determinism, and 3D layer usage, see the agent instructions in [`skills/multiplane/SKILL.md`](skills/multiplane/SKILL.md). This file is also a skill that agents can load.
|
|
112
|
+
|
|
113
|
+
## Commands
|
|
114
|
+
|
|
115
|
+
| Command | Purpose |
|
|
116
|
+
|---|---|
|
|
117
|
+
| `multiplane lint <shot>` | Static code checks without a browser |
|
|
118
|
+
| `multiplane render <shot>` | Render to MP4 |
|
|
119
|
+
| `multiplane stills <shot>` | Write PNG stills at given times plus a contact sheet; for checking frames or for final high-quality images |
|
|
120
|
+
| `multiplane verify <shot>` | Run a determinism check |
|
|
121
|
+
| `multiplane inspect <shot>` | Print a shot's duration, frame count, stage size, cuts and `meta` without rendering |
|
|
122
|
+
| `multiplane serve` | Serve shots on 127.0.0.1 for opening in a browser while writing them |
|
|
123
|
+
| `multiplane benchmark` | Measure rendering speed at different worker counts on this machine; later renders use the fastest |
|
|
124
|
+
| `multiplane doctor` | Check Node, the browser, GPU rasterization, FFmpeg, and optional three.js |
|
|
125
|
+
| `multiplane status <job>` | View job status |
|
|
126
|
+
| `multiplane cancel <job>` | Stop a job |
|
|
127
|
+
| `multiplane wait <job>` | Wait for a job to finish and print its events, for example after an agent session restarts |
|
|
128
|
+
| `multiplane config get\|set\|unset` | Read or change the user config |
|
|
129
|
+
| `multiplane skill status\|install\|remove` | Install, update or remove the agent skill for Claude Code and Codex |
|
|
130
|
+
|
|
131
|
+
Shot paths are relative to the resource root `--root` (the current directory by default). Each run has its own job directory, `out/jobs/<jobId>/`. Without `--out`, artifacts are written there (video, `stills/`, `verify/`), so runs do not overwrite each other. See `artifacts` in the result for the final paths. `--out` sets the output location. An existing target is rejected unless `--overwrite` is given. `render`, `stills`, and `verify` automatically run `lint` before starting and stop on errors. Use `--no-lint` to skip it. `npx multiplane <command> --help` lists all options for that command.
|
|
132
|
+
|
|
133
|
+
Common `render` options:
|
|
134
|
+
|
|
135
|
+
| Option | Meaning |
|
|
136
|
+
|---|---|
|
|
137
|
+
| `--from` / `--to` | Render a range only (seconds, end time excluded) |
|
|
138
|
+
| `--fps` | Override the shot's frame rate |
|
|
139
|
+
| `--scale` | Output scale; for example, `2` outputs a 1920×1080 shot at 3840×2160 |
|
|
140
|
+
| `--draft` | Fast preview preset: capture at a scale of at most 0.5 and upscale, no motion blur, no grain. Lower quality by design; explicit options override it. Also available for `stills` |
|
|
141
|
+
| `--render-scale`, `--upscale` | Capture at a lower scale, then upscale (`lanczos` / `neighbor`); the capture scale must not exceed `--scale` |
|
|
142
|
+
| `--motion-blur`, `--shutter`, `--adaptive-blur` | Time samples per frame, shutter fraction, adaptive sampling |
|
|
143
|
+
| `--quality` | Quality preset: `draft` (same as `--draft`), `standard` (default), `high` (rarely needed; see below). When the shot has motion blur, samples are added according to how far things move locally, up to 64 for `standard` and 256 for `high`; see below |
|
|
144
|
+
| `--max-motion-blur` | Custom limit for adding samples, overriding the preset; set it equal to `--motion-blur` for a fixed count |
|
|
145
|
+
| `--grain` | Film grain intensity |
|
|
146
|
+
| `--no-post` | Disable all post-processing |
|
|
147
|
+
| `--workers` | Number of parallel browser processes; by default the count measured by `multiplane benchmark`, or an estimate from CPU, memory and resolution; see "Parallel workers" below |
|
|
148
|
+
| `--raster` | `gpu` (default) or `software` |
|
|
149
|
+
| `--format`, `--alpha` | Output format `mp4` (default), `webm` (VP9) or `mov` (ProRes 4444); `--alpha` keeps transparency (WebM and MOV only, also for `stills` and `verify`). See "Transparent video" below |
|
|
150
|
+
| `--encoder`, `--max-bitrate` | Encoder, bitrate limit (Mbps) |
|
|
151
|
+
| `--audio`, `--audio-offset` | Mix in one audio track with an offset (seconds) |
|
|
152
|
+
|
|
153
|
+
The shot's `post` values are defaults. Command-line options override them.
|
|
154
|
+
|
|
155
|
+
## Machine-readable output
|
|
156
|
+
|
|
157
|
+
With `--json`, stdout contains one JSON event per line (NDJSON). The last line is always the final result:
|
|
158
|
+
|
|
159
|
+
```json
|
|
160
|
+
{"schema":"multiplane/1","type":"result","command":"render","ts":"2026-10-11T09:30:09.120Z","ok":true,"code":null,"message":null,"artifacts":[{"path":"/abs/out/jobs/20261011-093000-animation-3f2a/animation.mp4","kind":"video","bytes":81234}],"elapsedMs":3120,"jobId":"20261011-093000-animation-3f2a","data":{"frames":24}}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
- On failure, `ok` is `false`, `code` is a stable error code, and `hint` suggests a fix. Human-readable messages go to stderr.
|
|
164
|
+
- Exit codes: `0` success, `1` shot or check failure, `2` invalid arguments, `3` environment problem, `4` renderer internal error, `130` cancelled.
|
|
165
|
+
- Each run writes `events.jsonl` (the same event stream) and `status.json` (the latest snapshot) under `out/jobs/<jobId>/`, allowing other programs to show progress. Rendering also maintains a preview thumbnail, `preview.png`.
|
|
166
|
+
|
|
167
|
+
See [`docs/protocol.md`](https://github.com/LBEILC/Multiplane/blob/main/docs/protocol.md) for event types, fields, and all error codes.
|
|
168
|
+
|
|
169
|
+
## User config and job index
|
|
170
|
+
|
|
171
|
+
The renderer reads config and registers jobs in a user directory: `%USERPROFILE%\.multiplane` on Windows (`%APPDATA%\multiplane` up to 0.2.0; files there are copied over on first use), `~/Library/Application Support/multiplane` on macOS, and `~/.config/multiplane` on Linux. Override it with the environment variable `MULTIPLANE_HOME`.
|
|
172
|
+
|
|
173
|
+
- `config.json`: user defaults, with optional fields `ffmpeg`, `raster`, `encoder`, `workers`, and `jobsDir`. Priority: command-line options > environment variables > `config.json` > built-in defaults. Rendering never writes this file; `multiplane config` and the desktop app do. `doctor` and `config get` report its location and which fields take effect.
|
|
174
|
+
- `jobs.jsonl`: one line appended at the start and end of each job, allowing other programs to discover jobs across projects. The job directory's `status.json` is the authority for job status. Set `MULTIPLANE_NO_INDEX=1` to disable registration.
|
|
175
|
+
|
|
176
|
+
Read or write failures in this directory do not affect rendering; they only produce a warning. See section 8 of [`docs/protocol.md`](https://github.com/LBEILC/Multiplane/blob/main/docs/protocol.md) for the format.
|
|
177
|
+
|
|
178
|
+
## JS API
|
|
179
|
+
|
|
180
|
+
```js
|
|
181
|
+
import { inspectShot, lintShot, renderShot, stillsShot, verifyShot } from 'multiplane';
|
|
182
|
+
|
|
183
|
+
const result = await renderShot('examples/animation.html', {
|
|
184
|
+
workers: 2,
|
|
185
|
+
out: 'out/demo.mp4',
|
|
186
|
+
onEvent: event => { if (event.type === 'progress') console.error(event.done, event.total); },
|
|
187
|
+
});
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
The return value has the same structure as the `result` event, without `schema`, `type` and `ts`. Failures throw a `MultiplaneError` with a `code`.
|
|
191
|
+
|
|
192
|
+
## Transparent video
|
|
193
|
+
|
|
194
|
+
Render a shot with a transparent background to lay it over other footage:
|
|
195
|
+
|
|
196
|
+
```sh
|
|
197
|
+
npx multiplane render examples/alpha.html --format webm --alpha
|
|
198
|
+
npx multiplane render examples/alpha.html --format mov --alpha
|
|
199
|
+
npx multiplane stills examples/alpha.html --alpha --at 0,1
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
The page background becomes transparent; anything the shot paints itself, including a background colour on `#stage`, stays opaque, so leave `#stage` without a background for a fully transparent layer. A shot can make transparency its default with `post: { alpha: true }`.
|
|
203
|
+
|
|
204
|
+
- `webm` uses VP9 with alpha. Many players ignore VP9 alpha; to check it with FFmpeg, decode with `-c:v libvpx-vp9`, since the default VP9 decoder drops alpha.
|
|
205
|
+
- `mov` uses ProRes 4444, which most video editors read with alpha.
|
|
206
|
+
- Motion blur and scaling are computed on premultiplied colour, so moving edges do not pick up dark or coloured fringes. Before publishing, the renderer decodes the first and middle frames and checks that alpha survived encoding (at most 2/255 off per pixel).
|
|
207
|
+
- Your FFmpeg build needs `libvpx-vp9` for WebM and `prores_ks` for MOV; `doctor` reports both.
|
|
208
|
+
|
|
209
|
+
## Quality and motion blur
|
|
210
|
+
|
|
211
|
+
Motion blur averages several captures taken during the shutter time of each frame. With too few samples, fast-moving things break into separate copies; with many samples everywhere, rendering is slow. Multiplane picks the number of samples for each frame from how far things move locally:
|
|
212
|
+
|
|
213
|
+
- The shot decides **whether** there is motion blur: `post.motionBlur` above 1 turns it on and is the starting number of samples. The shot also sets `shutter` and grain.
|
|
214
|
+
- `--quality` decides **how good** it is: frames are captured at the starting count, the image is compared block by block between adjacent samples, and wherever something moved more than about 2 pixels (`standard`) or 1 pixel (`high`), the count doubles, up to 64 or 256. Static frames use 1 sample and slow frames keep the starting count, so only fast frames take longer.
|
|
215
|
+
- The default `standard` is enough for iterating and for final videos. `high` takes several times longer on real shots for a gain you only see when zooming in; use it for stills that will be viewed up close or for extremely fast, thin motion. Use `--draft` to check composition.
|
|
216
|
+
- For full control, `--max-motion-blur` changes the limit, and setting it equal to `--motion-blur` gives a fixed count. A shot with `blurAt(t)` uses its own counts and is never raised.
|
|
217
|
+
- `blurSamples` in the result lists how many samples frames actually used.
|
|
218
|
+
|
|
219
|
+
## Parallel workers
|
|
220
|
+
|
|
221
|
+
`render` and `verify` split frames across several browser processes (`--workers`). More processes are not always faster: with GPU rasterization they all share one graphics card, and too many slow things down.
|
|
222
|
+
|
|
223
|
+
- **Measure once on your machine**: `multiplane benchmark` (about 2–3 minutes) renders an ordinary web page, a Canvas shot and a 3D shot with 1, 2, 4, 6, 8, 12, 16 and 24 processes, and keeps, for each kind, the smallest count within 95% of the fastest. Renders then use that count for the kind of shot whenever `--workers` and the config leave it unset.
|
|
224
|
+
- If memory runs out during the measurement, it stops at the largest count that fits and recommends from the counts measured; `doctor` notes the memory limit.
|
|
225
|
+
- Without a measurement, the count is estimated from CPU cores, free memory and output size, at most 12.
|
|
226
|
+
- After a change of computer, graphics card, browser or Multiplane version, `doctor` reports the measurement as outdated; run it again. The result is stored as `benchmark.json` in the user directory.
|
|
227
|
+
- `resources.workersSource` in the result says where the count came from: `option` (command line), `config`, `benchmark` or `estimate`.
|
|
228
|
+
- The worker count changes speed only, never pixels: a shot renders identically with any number of processes.
|
|
229
|
+
|
|
230
|
+
## Stills
|
|
231
|
+
|
|
232
|
+
`stills` is not only for checking frames. Each PNG goes through the same post-processing as the same frame of a video, so it can be the final deliverable: a cover, a poster, an illustration, an image for social media.
|
|
233
|
+
|
|
234
|
+
```sh
|
|
235
|
+
# One still each at 0, 1.5 and 3 seconds
|
|
236
|
+
npx multiplane stills shot.html --at 0,1.5,3 --out out/stills
|
|
237
|
+
|
|
238
|
+
# A cover: 2x supersampling (a 1920×1080 shot becomes 3840×2160), with motion blur
|
|
239
|
+
npx multiplane stills shot.html --at 2.4 --scale 2 --motion-blur 16 --out out/cover
|
|
240
|
+
|
|
241
|
+
# A PNG with a transparent background, for laying over other footage
|
|
242
|
+
npx multiplane stills shot.html --at 1 --alpha --out out/overlay
|
|
243
|
+
|
|
244
|
+
# One still every 0.5 seconds, to skim through the whole shot
|
|
245
|
+
npx multiplane stills shot.html --every 0.5
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
- Files are named `t-<seconds>.png` (for example `t-2.40.png`), next to a contact sheet of all of them, `_sheet.png`.
|
|
249
|
+
- `--quality`, `--scale`, `--motion-blur`, `--max-motion-blur`, `--shutter`, `--grain` and `--alpha` work as they do for `render`, and so does depth of field in the 3D layer. Defaults the shot declares in `post` apply too.
|
|
250
|
+
- For visible motion blur in a still, pick a time during motion; at static times the sample count drops to 1 automatically and the image is as sharp as without blur.
|
|
251
|
+
- Add `--quality high` for stills that will be viewed up close; `--draft` is for checking composition only.
|
|
252
|
+
|
|
253
|
+
## 3D layer
|
|
254
|
+
|
|
255
|
+
```sh
|
|
256
|
+
npm install three@0.180.0
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
`multiplane/three` adds a transparent WebGL canvas to the stage, providing image cards, surface annotations, and accumulated depth of field. It can be layered with ordinary HTML elements. Shots that do not use 3D do not load it. See [`examples/three-cards.html`](examples/three-cards.html) for an example and the skill's "3D layer" section for usage.
|
|
260
|
+
|
|
261
|
+
## Desktop app
|
|
262
|
+
|
|
263
|
+
`app/` contains the companion Multiplane desktop app (Electron), intended for people rather than agents:
|
|
264
|
+
|
|
265
|
+
<picture>
|
|
266
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/LBEILC/Multiplane/main/docs/images/app-jobs-en-dark.png">
|
|
267
|
+
<img src="https://raw.githubusercontent.com/LBEILC/Multiplane/main/docs/images/app-jobs-en-light.png" width="800" alt="The Jobs page of the Multiplane desktop app: a running job, a queued job and a live preview frame">
|
|
268
|
+
</picture>
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
- **Jobs**: list render, stills, and check jobs across projects, show progress and preview frames in real time, view finished videos, contact sheets, and diff images from failed checks, and stop running jobs.
|
|
272
|
+
- **Environment**: check whether the browser, GPU rasterization, FFmpeg, and other requirements are ready. Install and update the Multiplane skill for Claude Code (`~/.claude/skills/`) and Codex (`~/.agents/skills/`), without directly overwriting user modifications.
|
|
273
|
+
- **Settings**: write the user config `config.json` described above.
|
|
274
|
+
- **Hosted renders** (off by default): when turned on in Settings, renders started by an agent are run by the app instead of the agent's session, so they keep going when the session ends. One hosted render runs at a time and the rest wait in a queue. The agent's command and output stay the same, and `multiplane wait <job>` picks a job up again after a session restarts. With hosting on, closing the window keeps the app in the system tray. See section 11 of the protocol.
|
|
275
|
+
- **Updates**: the app checks GitHub for a new release at startup and once a day (can be turned off). On Windows it downloads and installs the update in the app; on macOS and Linux it links to the download page.
|
|
276
|
+
- The interface is in English and Simplified Chinese. It follows the system language and can be switched in Settings.
|
|
277
|
+
|
|
278
|
+
It communicates with the renderer only through the files and commands in [`docs/protocol.md`](https://github.com/LBEILC/Multiplane/blob/main/docs/protocol.md). It is not required for rendering: agents can render from the command line without it.
|
|
279
|
+
|
|
280
|
+
Download an installer from the [Releases page](https://github.com/LBEILC/Multiplane/releases). The installers bundle a renderer of the same version, so the app works without a separate Node.js install. That renderer is also available as a command: `multiplane.cmd` in the install directory on Windows, `Multiplane.app/Contents/Resources/bin/multiplane` on macOS, and `Multiplane-<version>-x86_64.AppImage --multiplane-cli` on Linux. The Environment page shows the exact path when `multiplane` is not on PATH. FFmpeg is not bundled (see above), and Chromium is downloaded on first use from the Environment page.
|
|
281
|
+
|
|
282
|
+
| System | Package | First launch (the packages are not code-signed) |
|
|
283
|
+
|---|---|---|
|
|
284
|
+
| Windows | NSIS installer (`.exe`) | SmartScreen shows a warning: choose "More info", then "Run anyway". |
|
|
285
|
+
| macOS | `.dmg`, one for Apple silicon and one for Intel | Gatekeeper blocks the first launch: right-click the app and choose "Open", or allow it under System Settings → Privacy & Security. |
|
|
286
|
+
| Linux | AppImage | Make it executable (`chmod +x`), then run it. It needs FUSE. Without FUSE, extract it once with `--appimage-extract` and run `squashfs-root/AppRun` from then on (do not open two copies with `--appimage-extract-and-run`: the one that exits deletes the shared temporary directory and breaks the other). On systems where unprivileged user namespaces are unavailable, the AppImage launcher starts the app with the Chromium sandbox turned off. |
|
|
287
|
+
|
|
288
|
+
To build a package for the system you are on, or to run the app during development:
|
|
289
|
+
|
|
290
|
+
```sh
|
|
291
|
+
npm --prefix app install
|
|
292
|
+
npm --prefix app run dist
|
|
293
|
+
npm --prefix app start
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
`dist` writes the package to `app/release/`. `npm --prefix app start` uses separate development directories for the renderer config, job index and app data; add `-- --use-user-home` to use your real ones.
|
|
297
|
+
|
|
298
|
+
## Examples
|
|
299
|
+
|
|
300
|
+
| File | Content |
|
|
301
|
+
|---|---|
|
|
302
|
+
| [`examples/animation.html`](examples/animation.html) | 2D geometric animation with one hard cut in the middle, demonstrating motion blur that respects cuts |
|
|
303
|
+
| [`examples/typography.html`](examples/typography.html) | Typography with a local font (Roboto, OFL) |
|
|
304
|
+
| [`examples/three-cards.html`](examples/three-cards.html) | 3D cards and accumulated depth of field |
|
|
305
|
+
|
|
306
|
+
## Determinism and limitations
|
|
307
|
+
|
|
308
|
+
- Pixel equality is guaranteed only on **the same machine, with the same browser version, fonts, and assets**. Different GPUs, drivers, or browser versions may produce small differences.
|
|
309
|
+
- `lint` is a static check. It cannot detect indirect calls (such as `const now = Date.now; now()`) or accumulated state. `verify` is the final check.
|
|
310
|
+
- `verify` uses sampling: by default, it checks frames near 0%, 25%, 50%, and 75% of the duration, plus consecutive frames from the first second. Passing does not mean every time has been checked.
|
|
311
|
+
- Adaptive motion blur estimates the sample count from image changes. It is an approximation.
|
|
312
|
+
- With GPU rasterization (the default), a frame captured after a reload or in another process can differ in a few scattered pixels by a level or two (caused by the graphics card, invisible). `verify` reports it as a `gpu_raster_jitter` warning, not a failure; with `--raster software` it still requires identical pixels.
|
|
313
|
+
- Frames where nothing changes are reused instead of captured again, with periodic pixel checks; output is identical with `--no-static-reuse`. Shots with video, iframes or running CSS animations are always captured frame by frame. See section 12 of the protocol.
|
|
314
|
+
- The renderer and desktop app pass the same test suite in CI on Windows, macOS (Apple silicon) and Linux (x64, X11 under Xvfb). Windows 10/11 has also been verified on a real machine; macOS and Linux have not yet been tried on real machines, and Wayland has not been tested. CI on Linux has no GPU, so it covers software rasterization there; GPU rasterization on Linux is untested.
|
|
315
|
+
|
|
316
|
+
## Related projects
|
|
317
|
+
|
|
318
|
+
Multiplane did not invent the combination of HTML, a time function, a headless browser, and FFmpeg. Related projects include [HyperFrames](https://github.com/heygen-com/hyperframes), [Remotion](https://www.remotion.dev/), [Revideo](https://github.com/redotvideo/revideo), and [Motion Canvas](https://motioncanvas.io/), each with its own focus. Multiplane focuses on shots written as ordinary web pages without a framework dependency, verifiable determinism, motion blur that respects hard cuts, and accumulated depth of field in the 3D layer.
|
|
319
|
+
|
|
320
|
+
## License
|
|
321
|
+
|
|
322
|
+
[Apache-2.0](LICENSE). See [`THIRD_PARTY_NOTICES.md`](THIRD_PARTY_NOTICES.md) for third-party component and example font licenses. FFmpeg is not distributed with this package; its license depends on the build you use.
|