framefields 2.0.2 → 2.0.5
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/README.md +10 -32
- package/dist/audio/index.d.mts +4 -4
- package/dist/audio/index.mjs +10 -10
- package/dist/{audio-x28ZgQBP.mjs → audio-COkiXyDb.mjs} +3 -3
- package/dist/{audio-x28ZgQBP.mjs.map → audio-COkiXyDb.mjs.map} +1 -1
- package/dist/{audio-Dvwwz8xU.d.mts → audio-nmU-BWxI.d.mts} +3 -3
- package/dist/{audio-Dvwwz8xU.d.mts.map → audio-nmU-BWxI.d.mts.map} +1 -1
- package/dist/{dist-1iUYuPct.mjs → dist-55MP0aN4.mjs} +59 -16
- package/dist/dist-55MP0aN4.mjs.map +1 -0
- package/dist/{dist-gjHmorxL.mjs → dist-DgIKXWZ1.mjs} +75 -16
- package/dist/dist-DgIKXWZ1.mjs.map +1 -0
- package/dist/effects/index.d.mts +4 -4
- package/dist/effects/index.mjs +23 -23
- package/dist/{effects-CHWORCYd.mjs → effects-m3FuatqZ.mjs} +2 -2
- package/dist/{effects-CHWORCYd.mjs.map → effects-m3FuatqZ.mjs.map} +1 -1
- package/dist/fonts/index.d.mts +1 -1
- package/dist/fonts/index.mjs +1 -1
- package/dist/{fonts-DrGTBJJk.mjs → fonts-C_AWIMdb.mjs} +13 -2
- package/dist/fonts-C_AWIMdb.mjs.map +1 -0
- package/dist/{index-DKFGNyMB.d.mts → index-Bx7JBFT6.d.mts} +9 -2
- package/dist/index-Bx7JBFT6.d.mts.map +1 -0
- package/dist/{index-B0BNPEoH.d.mts → index-C8rstr3Y.d.mts} +5 -5
- package/dist/index-C8rstr3Y.d.mts.map +1 -0
- package/dist/{index-D2wJqSl5.d.mts → index-CAGDH7l6.d.mts} +3 -3
- package/dist/{index-D2wJqSl5.d.mts.map → index-CAGDH7l6.d.mts.map} +1 -1
- package/dist/{index-DiSKzDGR.d.mts → index-CbzEM_o6.d.mts} +8 -1
- package/dist/index-CbzEM_o6.d.mts.map +1 -0
- package/dist/{index-BMVVmc4G.d.mts → index-DY3mLoPO.d.mts} +2 -2
- package/dist/{index-BMVVmc4G.d.mts.map → index-DY3mLoPO.d.mts.map} +1 -1
- package/dist/{index-D1ZsMpke.d.mts → index-i0xPY4Fe.d.mts} +3 -3
- package/dist/{index-D1ZsMpke.d.mts.map → index-i0xPY4Fe.d.mts.map} +1 -1
- package/dist/{index-D2jDC7O5.d.mts → index-ogL8Y-Ng.d.mts} +9 -1
- package/dist/index-ogL8Y-Ng.d.mts.map +1 -0
- package/dist/index.d.mts +88 -70
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +44 -39
- package/dist/index.mjs.map +1 -1
- package/dist/preview/index.d.mts +3 -3
- package/dist/preview/index.mjs +10 -10
- package/dist/{preview-uQM210iy.mjs → preview-BM0Zzcn1.mjs} +74 -66
- package/dist/preview-BM0Zzcn1.mjs.map +1 -0
- package/dist/preview-engine/{chunk-HAXK42HG.mjs → chunk-2RMSCL6V.mjs} +1 -1
- package/dist/preview-engine/chunk-45JOFIHE.mjs +8 -0
- package/dist/preview-engine/{chunk-MT2DZQHO.mjs → chunk-4Q7JG6WJ.mjs} +1 -1
- package/dist/preview-engine/chunk-7S6ZFYLI.mjs +418 -0
- package/dist/preview-engine/chunk-KNZU3Z2F.mjs +3 -0
- package/dist/preview-engine/{chunk-SWD76532.mjs → chunk-PCB2VB5K.mjs} +2 -2
- package/dist/preview-engine/{chunk-GYG2IIBR.mjs → chunk-RJJRPL42.mjs} +1 -1
- package/dist/preview-engine/chunk-UC7CSW2A.mjs +1 -0
- package/dist/preview-engine/{chunk-4BEJJKEU.mjs → chunk-YK4XHUP4.mjs} +7 -7
- package/dist/preview-engine/effects.mjs +1 -1
- package/dist/preview-engine/fonts.mjs +1 -1
- package/dist/preview-engine/framefields.mjs +84 -76
- package/dist/preview-engine/player.mjs +5 -5
- package/dist/preview-engine/{renderers-YVN3EIOP.mjs → renderers-76MPTVZK.mjs} +1 -1
- package/dist/preview-engine/{renderers-R3XEYD5V.mjs → renderers-LFA5NW4V.mjs} +1 -1
- package/dist/{program-Dl510wmI.mjs → program-DGqyGGvj.mjs} +2 -2
- package/dist/{program-Dl510wmI.mjs.map → program-DGqyGGvj.mjs.map} +1 -1
- package/dist/renderer/index.d.mts +2 -2
- package/dist/renderer/index.mjs +2 -2
- package/dist/renderer--fkO4O8d.mjs +3 -0
- package/dist/{renderer-D7EX8IjU.mjs → renderer-0gJC-sc8.mjs} +2 -2
- package/dist/{renderer-D7EX8IjU.mjs.map → renderer-0gJC-sc8.mjs.map} +1 -1
- package/dist/{renderer-7DOcIxFG.mjs → renderer-2r4EgfdZ.mjs} +2 -2
- package/dist/{renderer-7DOcIxFG.mjs.map → renderer-2r4EgfdZ.mjs.map} +1 -1
- package/dist/{renderer-COkcU3db.mjs → renderer-5OiVjQan.mjs} +4 -4
- package/dist/{renderer-COkcU3db.mjs.map → renderer-5OiVjQan.mjs.map} +1 -1
- package/dist/renderer-6x7CgHhJ.mjs +3 -0
- package/dist/renderer-B0RDWv1C.mjs +3 -0
- package/dist/{renderer-CMzEjdzi.mjs → renderer-B9l641o-.mjs} +2 -2
- package/dist/{renderer-CMzEjdzi.mjs.map → renderer-B9l641o-.mjs.map} +1 -1
- package/dist/renderer-BFOkGS0N.mjs +3 -0
- package/dist/{renderer-pxXf9dvk.mjs → renderer-BJiL-BQY.mjs} +2 -2
- package/dist/{renderer-pxXf9dvk.mjs.map → renderer-BJiL-BQY.mjs.map} +1 -1
- package/dist/{renderer-B1CvhW4p.mjs → renderer-BMrm2Zbz.mjs} +2 -2
- package/dist/{renderer-B1CvhW4p.mjs.map → renderer-BMrm2Zbz.mjs.map} +1 -1
- package/dist/{renderer-Cj-whdVr.mjs → renderer-BUHMkkLU.mjs} +2 -2
- package/dist/{renderer-Cj-whdVr.mjs.map → renderer-BUHMkkLU.mjs.map} +1 -1
- package/dist/{renderer-CB5aWAaf.mjs → renderer-BYsmLk63.mjs} +2 -2
- package/dist/{renderer-CB5aWAaf.mjs.map → renderer-BYsmLk63.mjs.map} +1 -1
- package/dist/renderer-BaoJiMJO.mjs +3 -0
- package/dist/{renderer-DbaQLI4z.mjs → renderer-BtkU0BT-.mjs} +2 -2
- package/dist/{renderer-DbaQLI4z.mjs.map → renderer-BtkU0BT-.mjs.map} +1 -1
- package/dist/{renderer-Dt6fzgX5.mjs → renderer-Bwsh5TUv.mjs} +2 -2
- package/dist/{renderer-Dt6fzgX5.mjs.map → renderer-Bwsh5TUv.mjs.map} +1 -1
- package/dist/{renderer-vCFTm0oe.mjs → renderer-C28Bab-M.mjs} +2 -2
- package/dist/{renderer-vCFTm0oe.mjs.map → renderer-C28Bab-M.mjs.map} +1 -1
- package/dist/{renderer-HFfcJlmR.mjs → renderer-C3L9SNRO.mjs} +2 -2
- package/dist/{renderer-HFfcJlmR.mjs.map → renderer-C3L9SNRO.mjs.map} +1 -1
- package/dist/renderer-C4O-L73n.mjs +3 -0
- package/dist/{renderer-CB7lH5K8.mjs → renderer-C9BKcHZb.mjs} +1 -1
- package/dist/renderer-CGVuhBbA.mjs +3 -0
- package/dist/renderer-CHKB-Or4.mjs +3 -0
- package/dist/renderer-CL9gPcfZ.mjs +3 -0
- package/dist/{renderer-BXMmISWG.mjs → renderer-CMM4DZ1i.mjs} +2 -2
- package/dist/{renderer-BXMmISWG.mjs.map → renderer-CMM4DZ1i.mjs.map} +1 -1
- package/dist/{renderer-C17wL9QX.mjs → renderer-COkFM1KL.mjs} +2 -2
- package/dist/{renderer-C17wL9QX.mjs.map → renderer-COkFM1KL.mjs.map} +1 -1
- package/dist/renderer-CSkFF-qe.mjs +3 -0
- package/dist/renderer-CU2c6zWc.mjs +3 -0
- package/dist/{renderer-CDOUAN7X.mjs → renderer-CXUohIJ9.mjs} +2 -2
- package/dist/{renderer-CDOUAN7X.mjs.map → renderer-CXUohIJ9.mjs.map} +1 -1
- package/dist/{renderer-ByDhfrPE.mjs → renderer-CeADvUU_.mjs} +2 -2
- package/dist/{renderer-ByDhfrPE.mjs.map → renderer-CeADvUU_.mjs.map} +1 -1
- package/dist/{renderer-CH8sdfYX.mjs → renderer-CgVdRbc-.mjs} +2 -2
- package/dist/{renderer-CH8sdfYX.mjs.map → renderer-CgVdRbc-.mjs.map} +1 -1
- package/dist/renderer-CiLiH6w_.mjs +3 -0
- package/dist/{renderer-Dp_FfvF-.mjs → renderer-CnRoblJF.mjs} +2 -2
- package/dist/{renderer-Dp_FfvF-.mjs.map → renderer-CnRoblJF.mjs.map} +1 -1
- package/dist/{renderer-BAQmR0Zo.mjs → renderer-CsQ7nY0z.mjs} +1 -1
- package/dist/{renderer-CclIvsLO.mjs → renderer-CwAjqb2o.mjs} +1 -1
- package/dist/{renderer-CszOjcRW.mjs → renderer-Cx5H41JR.mjs} +1 -1
- package/dist/{renderer-Fco4EmAu.mjs → renderer-D1Zs78PQ.mjs} +126 -105
- package/dist/{renderer-Fco4EmAu.mjs.map → renderer-D1Zs78PQ.mjs.map} +1 -1
- package/dist/{renderer-BQHbp-Sp.mjs → renderer-D1ib-wB3.mjs} +2 -2
- package/dist/{renderer-BQHbp-Sp.mjs.map → renderer-D1ib-wB3.mjs.map} +1 -1
- package/dist/{renderer-Tp6OXWNZ.mjs → renderer-D54yO_M_.mjs} +2 -2
- package/dist/{renderer-Tp6OXWNZ.mjs.map → renderer-D54yO_M_.mjs.map} +1 -1
- package/dist/{renderer-Cl1oUXMi.mjs → renderer-D5N2JO7g.mjs} +2 -2
- package/dist/{renderer-Cl1oUXMi.mjs.map → renderer-D5N2JO7g.mjs.map} +1 -1
- package/dist/{renderer-BsgIcBKH.mjs → renderer-DBp6A7tR.mjs} +2 -2
- package/dist/{renderer-BsgIcBKH.mjs.map → renderer-DBp6A7tR.mjs.map} +1 -1
- package/dist/{renderer-vvpdx1vk.mjs → renderer-DI2RcpKU.mjs} +2 -2
- package/dist/{renderer-vvpdx1vk.mjs.map → renderer-DI2RcpKU.mjs.map} +1 -1
- package/dist/renderer-DMaYsSM4.mjs +3 -0
- package/dist/renderer-DZ7t_l1d.mjs +3 -0
- package/dist/{renderer-Ryo2CEFD.mjs → renderer-D_QS6p1q.mjs} +2 -2
- package/dist/{renderer-Ryo2CEFD.mjs.map → renderer-D_QS6p1q.mjs.map} +1 -1
- package/dist/{renderer-DDp7_TUi.d.mts → renderer-DcPOsAVu.d.mts} +2 -2
- package/dist/{renderer-DDp7_TUi.d.mts.map → renderer-DcPOsAVu.d.mts.map} +1 -1
- package/dist/{renderer-B_NuVaIP.mjs → renderer-Dcd-Pkzd.mjs} +2 -2
- package/dist/{renderer-B_NuVaIP.mjs.map → renderer-Dcd-Pkzd.mjs.map} +1 -1
- package/dist/{renderer-WkVrkCVA.mjs → renderer-DjiUZ0Vs.mjs} +2 -2
- package/dist/{renderer-WkVrkCVA.mjs.map → renderer-DjiUZ0Vs.mjs.map} +1 -1
- package/dist/renderer-DkJEnKQu.mjs +3 -0
- package/dist/renderer-DlkrKqQj.mjs +3 -0
- package/dist/renderer-DxOQ5S8T.mjs +3 -0
- package/dist/renderer-Ibvw1tE_.mjs +3 -0
- package/dist/{renderer-CwQhzcUn.mjs → renderer-JjMO41Cv.mjs} +2 -2
- package/dist/{renderer-CwQhzcUn.mjs.map → renderer-JjMO41Cv.mjs.map} +1 -1
- package/dist/renderer-M0jeFo6S.mjs +4 -0
- package/dist/renderer-NhSzqWsQ.mjs +3 -0
- package/dist/renderer-OrhgkzVu.mjs +3 -0
- package/dist/{renderer-DxjVQV6f.mjs → renderer-Q7_DUcnu.mjs} +2 -2
- package/dist/{renderer-DxjVQV6f.mjs.map → renderer-Q7_DUcnu.mjs.map} +1 -1
- package/dist/{renderer-I-Sw3JBB.mjs → renderer-QfSB-cRU.mjs} +2 -2
- package/dist/{renderer-I-Sw3JBB.mjs.map → renderer-QfSB-cRU.mjs.map} +1 -1
- package/dist/{renderer-OWQmmg4Z.mjs → renderer-WXylxlu2.mjs} +2 -2
- package/dist/{renderer-OWQmmg4Z.mjs.map → renderer-WXylxlu2.mjs.map} +1 -1
- package/dist/renderer-WrFAaLxW.mjs +3 -0
- package/dist/renderer-bbOALT4F.mjs +3 -0
- package/dist/{renderer-BGXU6vcW.mjs → renderer-bebAt6Dz.mjs} +2 -2
- package/dist/{renderer-BGXU6vcW.mjs.map → renderer-bebAt6Dz.mjs.map} +1 -1
- package/dist/{renderer-B9-QSJcG.mjs → renderer-eC1gRPRo.mjs} +2 -2
- package/dist/{renderer-B9-QSJcG.mjs.map → renderer-eC1gRPRo.mjs.map} +1 -1
- package/dist/renderer-lXmyzdcx.mjs +3 -0
- package/dist/renderer-ospMsCzr.mjs +3 -0
- package/dist/{renderer-k2JL79kM.mjs → renderer-vUjNMbLq.mjs} +2 -2
- package/dist/{renderer-k2JL79kM.mjs.map → renderer-vUjNMbLq.mjs.map} +1 -1
- package/dist/renderer-xc9wkiC7.mjs +3 -0
- package/dist/{renderer-XcmYTd6P.mjs → renderer-z6Qb5Z-W.mjs} +143 -4
- package/dist/renderer-z6Qb5Z-W.mjs.map +1 -0
- package/dist/{renderers-BWg16W-V-BXG_cjhM.mjs → renderers-BWg16W-V-jhdCp6Vd.mjs} +2 -2
- package/dist/{renderers-BWg16W-V-BXG_cjhM.mjs.map → renderers-BWg16W-V-jhdCp6Vd.mjs.map} +1 -1
- package/dist/signals/index.d.mts +5 -5
- package/dist/signals/index.mjs +4 -4
- package/package.json +55 -55
- package/dist/dist-1iUYuPct.mjs.map +0 -1
- package/dist/dist-gjHmorxL.mjs.map +0 -1
- package/dist/fonts-DrGTBJJk.mjs.map +0 -1
- package/dist/index-B0BNPEoH.d.mts.map +0 -1
- package/dist/index-D2jDC7O5.d.mts.map +0 -1
- package/dist/index-DKFGNyMB.d.mts.map +0 -1
- package/dist/index-DiSKzDGR.d.mts.map +0 -1
- package/dist/preview-engine/chunk-47VT677S.mjs +0 -8
- package/dist/preview-engine/chunk-DPST2XLC.mjs +0 -418
- package/dist/preview-engine/chunk-DRLEOHN5.mjs +0 -1
- package/dist/preview-engine/chunk-SZ7YRPKO.mjs +0 -3
- package/dist/preview-uQM210iy.mjs.map +0 -1
- package/dist/renderer-3BNskNuJ.mjs +0 -3
- package/dist/renderer-9dHlMQGX.mjs +0 -3
- package/dist/renderer-BGvORwkQ.mjs +0 -3
- package/dist/renderer-BLikd6BK.mjs +0 -3
- package/dist/renderer-BSbTJBWc.mjs +0 -3
- package/dist/renderer-BU1rwNRT.mjs +0 -3
- package/dist/renderer-BZJm3m4f.mjs +0 -3
- package/dist/renderer-BqyAgNt3.mjs +0 -3
- package/dist/renderer-BryTM2g7.mjs +0 -3
- package/dist/renderer-CQCoggIa.mjs +0 -3
- package/dist/renderer-CV2m6k1V.mjs +0 -3
- package/dist/renderer-CYY-poLe.mjs +0 -3
- package/dist/renderer-C_Ki91kc.mjs +0 -3
- package/dist/renderer-CmApuKjS.mjs +0 -3
- package/dist/renderer-Cu074POw.mjs +0 -3
- package/dist/renderer-D4qLmox-.mjs +0 -3
- package/dist/renderer-D6cJ6MSq.mjs +0 -3
- package/dist/renderer-DQLGnEFc.mjs +0 -3
- package/dist/renderer-DY2SVYUW.mjs +0 -3
- package/dist/renderer-DoAY5JbB.mjs +0 -4
- package/dist/renderer-DuplLRbp.mjs +0 -3
- package/dist/renderer-DvfUbZ31.mjs +0 -3
- package/dist/renderer-Q_uUQipO.mjs +0 -3
- package/dist/renderer-UQFCgeNx.mjs +0 -3
- package/dist/renderer-XcmYTd6P.mjs.map +0 -1
- package/dist/renderer-eMsnzJg9.mjs +0 -3
- package/dist/renderer-pHiz78e0.mjs +0 -3
package/README.md
CHANGED
|
@@ -32,18 +32,18 @@ Every frame of these films is rendered by framefields from TypeScript in [`examp
|
|
|
32
32
|
<table>
|
|
33
33
|
<tr>
|
|
34
34
|
<td width="33%" align="center">
|
|
35
|
-
<a href="https://youtu.be/
|
|
36
|
-
<br><a href="https://youtu.be/
|
|
35
|
+
<a href="https://youtu.be/FVXTqAozAv8"><img src="https://raw.githubusercontent.com/gatewai-dev/framefields/main/assets/showcase/framefields-launch.jpg" alt="Framefields launch film" width="100%"></a>
|
|
36
|
+
<br><a href="https://youtu.be/FVXTqAozAv8"><b>Framefields launch</b></a>
|
|
37
37
|
<br><sub><a href="https://github.com/gatewai-dev/framefields/blob/main/examples/22_framefields_launch"><code>22_framefields_launch</code></a></sub>
|
|
38
38
|
</td>
|
|
39
39
|
<td width="33%" align="center">
|
|
40
|
-
<a href="https://youtu.be/
|
|
41
|
-
<br><a href="https://youtu.be/
|
|
40
|
+
<a href="https://youtu.be/9BF1snfmg3o"><img src="https://raw.githubusercontent.com/gatewai-dev/framefields/main/assets/showcase/full-circle.jpg" alt="Full Circle film" width="100%"></a>
|
|
41
|
+
<br><a href="https://youtu.be/9BF1snfmg3o"><b>Full Circle</b></a>
|
|
42
42
|
<br><sub><a href="https://github.com/gatewai-dev/framefields/blob/main/examples/21_full_circle"><code>21_full_circle</code></a></sub>
|
|
43
43
|
</td>
|
|
44
44
|
<td width="33%" align="center">
|
|
45
|
-
<a href="https://youtu.be/
|
|
46
|
-
<br><a href="https://youtu.be/
|
|
45
|
+
<a href="https://youtu.be/RQ3yi_lzPqI"><img src="https://raw.githubusercontent.com/gatewai-dev/framefields/main/assets/showcase/dancer.jpg" alt="Dancer showcase film" width="100%"></a>
|
|
46
|
+
<br><a href="https://youtu.be/RQ3yi_lzPqI"><b>Dancer showcase</b></a>
|
|
47
47
|
<br><sub><a href="https://github.com/gatewai-dev/framefields/blob/main/examples/19_framefields_film"><code>19_framefields_film</code></a></sub>
|
|
48
48
|
</td>
|
|
49
49
|
</tr>
|
|
@@ -52,6 +52,8 @@ Every frame of these films is rendered by framefields from TypeScript in [`examp
|
|
|
52
52
|
> [!NOTE]
|
|
53
53
|
> **Using an AI coding agent?** Install the framefields skills in one line.
|
|
54
54
|
>
|
|
55
|
+
> **Note:** Framefields plugin review in claude takes a bit long... Please use the alternative skills installation for now.
|
|
56
|
+
>
|
|
55
57
|
> **Claude Code**
|
|
56
58
|
> ```text
|
|
57
59
|
> /plugin install framefields
|
|
@@ -69,7 +71,6 @@ Every frame of these films is rendered by framefields from TypeScript in [`examp
|
|
|
69
71
|
## Table of Contents
|
|
70
72
|
|
|
71
73
|
- [Why Framefields](#why-framefields)
|
|
72
|
-
- [Architectural Comparison](#architectural-comparison-framefields-vs-remotion-vs-hyperframes)
|
|
73
74
|
- [Key Features & Capabilities](#key-features--capabilities)
|
|
74
75
|
- [1. Slug GPU Vector Typography & After Effects Animators](#1-slug-gpu-vector-typography--after-effects-animators)
|
|
75
76
|
- [2. Photoshop-Inspired WebGPU 2D VFX](#2-photoshop-inspired-webgpu-2d-vfx-50-shaders)
|
|
@@ -113,30 +114,6 @@ Modern automated video generation is usually constrained by the architectures of
|
|
|
113
114
|
|
|
114
115
|
---
|
|
115
116
|
|
|
116
|
-
## Architectural Comparison: Framefields vs. Remotion vs. Hyperframes
|
|
117
|
-
|
|
118
|
-
Developers generating video programmatically commonly weigh **Remotion** (React/Chromium) or **Hyperframes** (Canvas2D/SVG web animation). The matrix below compares the fundamental engineering dimensions.
|
|
119
|
-
|
|
120
|
-
### Detailed Comparison Matrix
|
|
121
|
-
|
|
122
|
-
| Capability / Dimension | **Framefields** | **Remotion** | **Hyperframes** |
|
|
123
|
-
|---|---|---|---|
|
|
124
|
-
| **Underlying Engine** | **Native WebGPU** (WGSL compute & render pipelines via Dawn / Metal / Vulkan) | **Chromium / Puppeteer** (React DOM, HTML/CSS layout) | **Canvas2D / WebGL / SVG** (browser or Node Skia) |
|
|
125
|
-
| **Rendering Architecture** | Direct hardware framebuffer rendering & hardware video encoding (`@napi-rs/webcodecs`) | Spawns headless Chrome; captures frames via CDP / `page.screenshot()` | Software or hardware 2D canvas context |
|
|
126
|
-
| **Throughput** | **60–120+ FPS** (real-time to faster-than-real-time GPU execution) | **5–20 FPS** (DOM reflow, IPC, rasterization) | **20–40 FPS** (CPU draw commands / JS) |
|
|
127
|
-
| **Memory Footprint** | **~200–400 MB** per render (zero browser) | **1.5–4.0 GB+** per worker (Chromium + V8 DOM heap) | **~500 MB–1 GB** (Skia/Canvas bindings) |
|
|
128
|
-
| **Typography Engine** | **Slug GPU** — analytic Bézier evaluation in WGSL, infinite zoom, After Effects selectors | Browser DOM text (CSS fonts, rasterized, blurry under 3D transforms) | Canvas2D / path text (CPU-rasterized glyphs) |
|
|
129
|
-
| **2D VFX & Post-Processing** | **50+ WebGPU shaders** (Curves, Levels, Selective Color, 3D LUT, Film Grain, Halftone, Liquify, PBR Glass, Relight) | CSS Filters or custom WebGL canvas wrappers | Basic Canvas2D composites and 2D filters |
|
|
130
|
-
| **3D Graphics & Depth** | **Native 3D scene graph** — LookAt/Turntable camera, multiplane, skinning (OBJ/FBX/glTF), SSAO, PCSS, DoF | None built-in (embed Three.js/Fiber inside React DOM) | Minimal 2.5D layers; no unified mesh pipeline |
|
|
131
|
-
| **Motion Blur & Physics** | Physical 180° shutter velocity buffers in MRT + closed-form spring kinematics | CSS transitions / JS interpolation; synthetic blur hacks | Frame interpolation or manual multipass |
|
|
132
|
-
| **Audio Engine & DSP** | Native audio DSP & procedural SFX (multi-track mixing, beat grids, reactive signals) | `<Audio>` playback; basic volume curves | Basic static audio playback |
|
|
133
|
-
| **Charts & Data Viz** | **`Layer.chart`** — line, area, bar, scatter, candlestick, pie and donut charts built from native vector nodes, with staggered reveal animations | DOM chart libraries (Recharts, Chart.js) | Custom canvas draw operations |
|
|
134
|
-
| **AI & Computer Vision** | **On-device ONNX vision** — COCO-80 detection + instance masks (RTMDet-Ins), COCO-17 pose (RTMO), person mattes (Selfie Segmenter); WebGPU tensor conditioning (Canny, depth-to-normals, optical flow, deflicker) | External pre-rendered assets; no native GPU tensor conditioning | External pre-rendered assets |
|
|
135
|
-
| **Headless Verification** | **FrameGrid contact sheets**, single-frame snapshots, Skia MSE pixel-invariant assertions | Playwright/Puppeteer visual snapshots | Manual frame inspection / canvas diffing |
|
|
136
|
-
| **Docker / Cloud Portability** | **Compact** (~500 MB slim image with native GPU/Vulkan drivers) | **Heavy** (~2–3 GB with Chromium, fonts, X11/Mesa) | Moderate container size |
|
|
137
|
-
|
|
138
|
-
---
|
|
139
|
-
|
|
140
117
|
## Key Features & Capabilities
|
|
141
118
|
|
|
142
119
|
### 1. Slug GPU Vector Typography & After Effects Animators
|
|
@@ -262,12 +239,13 @@ Project normalized landmarks to screen space with a configurable camera FOV, the
|
|
|
262
239
|
- **FrameGrid contact sheets** — `comp.renderFrameGrid(...)` outputs sequential-frame contact sheets for instant review of easing, kinetic type, and transitions.
|
|
263
240
|
|
|
264
241
|
### 8. Live Preview in the Browser
|
|
265
|
-
- **Runs your composition, not a video** — `startPreview({ entry, export })` serves a localhost WebGPU player that loads the composition's own module and renders
|
|
242
|
+
- **Runs your composition, not a video** — `startPreview({ entry, export })` serves a localhost WebGPU player that loads the composition's own module and renders its frames live in the browser. Nothing is streamed: the server only hands over the bundle, the project's assets, and the soundtrack mixed by the export engine.
|
|
266
243
|
- **Timeline, waveform & frame stepping** — play/pause, scrub, step frame by frame, and read resolution, FPS, duration, and audio status at a glance.
|
|
267
244
|
- **One stable URL per project** — the port is derived from the working directory, so re-running the preview replaces the running server and any open tab reloads into the new version by itself. Close the tab and the server shuts down about five seconds later. A reload comes back at the same moment, and `#t=12.5` links to one.
|
|
268
245
|
- **Notes pinned on the picture** — pause, click a spot (or drag an area) and write what should change. Each note keeps its moment, its spot and a picture of the frame with the spot marked, in `.framefields/preview-notes/`, and prints where the preview runs, so an agent can apply exactly what you meant. Tick notes off as you check them.
|
|
269
246
|
- **Tells you what happened** — the page's errors (a failed build, a frame that threw, a missing font) and why the server stopped are printed where the preview runs, so whoever started it, person or agent, sees them without opening devtools. Scripts can drive the page through `window.framefieldsPreview` (`seek`, `play`, `pause`, `state`).
|
|
270
247
|
- **Shown where you are** — `startPreview` serves the page and returns its URL instead of opening a browser, so an agent can show it in its own pane (Claude Code, Codex); pass `open: true` to open the system browser.
|
|
248
|
+
- **Plays in real time, even on a slow device** — Play draws the frame due on the soundtrack's clock and skips the ones it missed, so playback starts at once and the sound never waits. A device that can't keep up shows fewer frames, and the page shows the frame rate it reaches.
|
|
271
249
|
|
|
272
250
|
```typescript
|
|
273
251
|
import { startPreview } from "framefields";
|
package/dist/audio/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { P as WebGPUAudioProcessor } from "../index-
|
|
2
|
-
import { a as SfxTrigger, c as softLimit, d as _default$5, f as _default$4, g as mixAudioTracks, h as _default$2, i as SfxRenderContext, l as _default$7, m as _default$3, n as encodeStereoWav, o as mixSfxInto, p as _default, r as RenderedSfx, s as renderSfx, t as EncodeWavOptions, u as _default$6 } from "../index-
|
|
3
|
-
import "../index-
|
|
4
|
-
import { t as _default$1 } from "../renderer-
|
|
1
|
+
import { P as WebGPUAudioProcessor } from "../index-ogL8Y-Ng.mjs";
|
|
2
|
+
import { a as SfxTrigger, c as softLimit, d as _default$5, f as _default$4, g as mixAudioTracks, h as _default$2, i as SfxRenderContext, l as _default$7, m as _default$3, n as encodeStereoWav, o as mixSfxInto, p as _default, r as RenderedSfx, s as renderSfx, t as EncodeWavOptions, u as _default$6 } from "../index-CAGDH7l6.mjs";
|
|
3
|
+
import "../index-DY3mLoPO.mjs";
|
|
4
|
+
import { t as _default$1 } from "../renderer-DcPOsAVu.mjs";
|
|
5
5
|
export { EncodeWavOptions, RenderedSfx, SfxRenderContext, SfxTrigger, WebGPUAudioProcessor, _default as audioFadeRenderer, _default$1 as audioSignalExtractorRenderer, _default$2 as compressorRenderer, _default$3 as delayRenderer, encodeStereoWav, mixAudioTracks, mixSfxInto, _default$4 as noiseGateRenderer, _default$5 as parametricEqRenderer, renderSfx, _default$6 as reverbRenderer, softLimit, _default$7 as stereoPanningRenderer };
|
package/dist/audio/index.mjs
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import { C as WebGPUAudioProcessor } from "../dist-
|
|
2
|
-
import { t as renderers_default$1 } from "../renderers-BWg16W-V-
|
|
3
|
-
import { i as mixAudioTracks } from "../dist-
|
|
4
|
-
import { t as renderers_default$2 } from "../renderer-
|
|
5
|
-
import { t as renderers_default$3 } from "../renderer-
|
|
6
|
-
import { t as renderers_default } from "../renderer-
|
|
7
|
-
import { t as renderers_default$4 } from "../renderer-
|
|
8
|
-
import { t as renderers_default$5 } from "../renderer-
|
|
9
|
-
import { t as renderers_default$6 } from "../renderer-
|
|
1
|
+
import { C as WebGPUAudioProcessor } from "../dist-DgIKXWZ1.mjs";
|
|
2
|
+
import { t as renderers_default$1 } from "../renderers-BWg16W-V-jhdCp6Vd.mjs";
|
|
3
|
+
import { i as mixAudioTracks } from "../dist-55MP0aN4.mjs";
|
|
4
|
+
import { t as renderers_default$2 } from "../renderer-D54yO_M_.mjs";
|
|
5
|
+
import { t as renderers_default$3 } from "../renderer-C28Bab-M.mjs";
|
|
6
|
+
import { t as renderers_default } from "../renderer-COkFM1KL.mjs";
|
|
7
|
+
import { t as renderers_default$4 } from "../renderer-2r4EgfdZ.mjs";
|
|
8
|
+
import { t as renderers_default$5 } from "../renderer-bebAt6Dz.mjs";
|
|
9
|
+
import { t as renderers_default$6 } from "../renderer-DI2RcpKU.mjs";
|
|
10
10
|
import "../renderer-C2n-EHxg.mjs";
|
|
11
|
-
import { t as renderers_default$7 } from "../renderer-
|
|
11
|
+
import { t as renderers_default$7 } from "../renderer-DjiUZ0Vs.mjs";
|
|
12
12
|
import { i as softLimit, n as mixSfxInto, r as renderSfx, t as encodeStereoWav } from "../audio-TAvPwzLR.mjs";
|
|
13
13
|
|
|
14
14
|
export { WebGPUAudioProcessor, renderers_default as audioFadeRenderer, renderers_default$1 as audioSignalExtractorRenderer, renderers_default$2 as compressorRenderer, renderers_default$3 as delayRenderer, encodeStereoWav, mixAudioTracks, mixSfxInto, renderers_default$4 as noiseGateRenderer, renderers_default$5 as parametricEqRenderer, renderSfx, renderers_default$6 as reverbRenderer, softLimit, renderers_default$7 as stereoPanningRenderer };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { _t as frameArraySignal } from "./dist-Bx6J0VcM.mjs";
|
|
2
|
-
import { F as ensureDevice, i as AudioSignalComputePipeline } from "./dist-
|
|
3
|
-
import { n as decodeAudioSource } from "./dist-
|
|
2
|
+
import { F as ensureDevice, i as AudioSignalComputePipeline } from "./dist-DgIKXWZ1.mjs";
|
|
3
|
+
import { n as decodeAudioSource } from "./dist-55MP0aN4.mjs";
|
|
4
4
|
|
|
5
5
|
//#region src/signals/audio.ts
|
|
6
6
|
const AudioSignal = {
|
|
@@ -82,4 +82,4 @@ const AudioSignal = {
|
|
|
82
82
|
|
|
83
83
|
//#endregion
|
|
84
84
|
export { AudioSignal as t };
|
|
85
|
-
//# sourceMappingURL=audio-
|
|
85
|
+
//# sourceMappingURL=audio-COkiXyDb.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audio-
|
|
1
|
+
{"version":3,"file":"audio-COkiXyDb.mjs","names":[],"sources":["../src/signals/audio.ts"],"sourcesContent":["import { decodeAudioSource } from \"@framefields/compositions\";\nimport {\n\ttype FrameSignal,\n\tframeArraySignal,\n\ttype GPUTextureBinding,\n} from \"@framefields/core\";\nimport {\n\ttype AudioSignalComputeConfig,\n\tAudioSignalComputePipeline,\n\tensureDevice,\n} from \"@framefields/webgpu-renderers\";\n\nexport interface AudioSignalOptions extends AudioSignalComputeConfig {\n\tfps?: number;\n\ttargetWidth?: number;\n\tpreviewMode?: \"waveform\" | \"envelope\" | \"beat_markers\" | \"spectrum\";\n}\n\nexport interface ExtractedAudioSignalsBundle {\n\tprimary: FrameSignal<number>;\n\tbeat: FrameSignal<number>;\n\tbass: FrameSignal<number>;\n\tenergy: FrameSignal<number>;\n\tdurationSec: number;\n\tnumFrames: number;\n\tsampleRate: number;\n\ttextureView: unknown;\n\ttexture: unknown;\n\tprimaryBuffer: unknown;\n\tbeatBuffer: unknown;\n\tbassBuffer: unknown;\n\tenergyBuffer: unknown;\n\tseekFrame(frame: number): void;\n}\n\nexport const AudioSignal = {\n\tasync extractFromChannels(\n\t\tchannels: Float32Array[],\n\t\tsampleRate: number,\n\t\toptions: AudioSignalOptions = {},\n\t): Promise<ExtractedAudioSignalsBundle> {\n\t\tconst device = await ensureDevice();\n\t\tconst fps = options.fps ?? 24;\n\t\tconst targetWidth = options.targetWidth ?? 512;\n\n\t\tconst extracted = await AudioSignalComputePipeline.extractFeatures(\n\t\t\tdevice,\n\t\t\tchannels,\n\t\t\tsampleRate,\n\t\t\tfps,\n\t\t\toptions,\n\t\t\ttargetWidth,\n\t\t);\n\n\t\tconst samples = extracted.channelSamples ?? {\n\t\t\tprimary: new Float32Array(extracted.numFrames),\n\t\t\tbeat: new Float32Array(extracted.numFrames),\n\t\t\tbass: new Float32Array(extracted.numFrames),\n\t\t\tenergy: new Float32Array(extracted.numFrames),\n\t\t};\n\n\t\tconst primaryBinding: GPUTextureBinding = {\n\t\t\tnodeId: \"audio_primary\",\n\t\t\tchannel: \"primary\",\n\t\t\ttextureView: extracted.textureView,\n\t\t\ttexture: extracted.texture,\n\t\t\tbuffer: extracted.primaryBuffer,\n\t\t\tstats: extracted.stats.primary,\n\t\t};\n\t\tconst beatBinding: GPUTextureBinding = {\n\t\t\tnodeId: \"audio_beat\",\n\t\t\tchannel: \"beat\",\n\t\t\ttextureView: extracted.textureView,\n\t\t\ttexture: extracted.texture,\n\t\t\tbuffer: extracted.beatBuffer,\n\t\t\tstats: extracted.stats.beat,\n\t\t};\n\t\tconst bassBinding: GPUTextureBinding = {\n\t\t\tnodeId: \"audio_bass\",\n\t\t\tchannel: \"bass\",\n\t\t\ttextureView: extracted.textureView,\n\t\t\ttexture: extracted.texture,\n\t\t\tbuffer: extracted.bassBuffer,\n\t\t\tstats: extracted.stats.bass,\n\t\t};\n\t\tconst energyBinding: GPUTextureBinding = {\n\t\t\tnodeId: \"audio_energy\",\n\t\t\tchannel: \"energy\",\n\t\t\ttextureView: extracted.textureView,\n\t\t\ttexture: extracted.texture,\n\t\t\tbuffer: extracted.energyBuffer,\n\t\t\tstats: extracted.stats.energy,\n\t\t};\n\n\t\tconst primary = frameArraySignal(samples.primary, fps, primaryBinding);\n\t\tconst beat = frameArraySignal(samples.beat, fps, beatBinding);\n\t\tconst bass = frameArraySignal(samples.bass, fps, bassBinding);\n\t\tconst energy = frameArraySignal(samples.energy, fps, energyBinding);\n\n\t\treturn {\n\t\t\tprimary,\n\t\t\tbeat,\n\t\t\tbass,\n\t\t\tenergy,\n\t\t\tdurationSec: extracted.durationSec,\n\t\t\tnumFrames: extracted.numFrames,\n\t\t\tsampleRate,\n\t\t\ttextureView: extracted.textureView,\n\t\t\ttexture: extracted.texture,\n\t\t\tprimaryBuffer: extracted.primaryBuffer,\n\t\t\tbeatBuffer: extracted.beatBuffer,\n\t\t\tbassBuffer: extracted.bassBuffer,\n\t\t\tenergyBuffer: extracted.energyBuffer,\n\t\t\tseekFrame(frame: number) {\n\t\t\t\tprimary.seekFrame?.(frame);\n\t\t\t\tbeat.seekFrame?.(frame);\n\t\t\t\tbass.seekFrame?.(frame);\n\t\t\t\tenergy.seekFrame?.(frame);\n\t\t\t},\n\t\t};\n\t},\n\n\tasync extract(\n\t\tsource: string,\n\t\toptions: AudioSignalOptions = {},\n\t): Promise<ExtractedAudioSignalsBundle> {\n\t\tconst decoded = await decodeAudioSource(source);\n\t\tif (!decoded || decoded.channels.length === 0) {\n\t\t\tthrow new Error(\n\t\t\t\t`Failed to decode audio source for feature extraction: ${source}`,\n\t\t\t);\n\t\t}\n\t\treturn AudioSignal.extractFromChannels(\n\t\t\tdecoded.channels,\n\t\t\tdecoded.sampleRate,\n\t\t\toptions,\n\t\t);\n\t},\n};\n"],"mappings":";;;;;AAmCA,MAAa,cAAc;CAC1B,MAAM,oBACL,UACA,YACA,UAA8B,EAAE,EACO;EACvC,MAAM,SAAS,MAAM,cAAc;EACnC,MAAM,MAAM,QAAQ,OAAO;EAC3B,MAAM,cAAc,QAAQ,eAAe;EAE3C,MAAM,YAAY,MAAM,2BAA2B,gBAClD,QACA,UACA,YACA,KACA,SACA,YACA;EAED,MAAM,UAAU,UAAU,kBAAkB;GAC3C,SAAS,IAAI,aAAa,UAAU,UAAU;GAC9C,MAAM,IAAI,aAAa,UAAU,UAAU;GAC3C,MAAM,IAAI,aAAa,UAAU,UAAU;GAC3C,QAAQ,IAAI,aAAa,UAAU,UAAU;GAC7C;EAED,MAAM,iBAAoC;GACzC,QAAQ;GACR,SAAS;GACT,aAAa,UAAU;GACvB,SAAS,UAAU;GACnB,QAAQ,UAAU;GAClB,OAAO,UAAU,MAAM;GACvB;EACD,MAAM,cAAiC;GACtC,QAAQ;GACR,SAAS;GACT,aAAa,UAAU;GACvB,SAAS,UAAU;GACnB,QAAQ,UAAU;GAClB,OAAO,UAAU,MAAM;GACvB;EACD,MAAM,cAAiC;GACtC,QAAQ;GACR,SAAS;GACT,aAAa,UAAU;GACvB,SAAS,UAAU;GACnB,QAAQ,UAAU;GAClB,OAAO,UAAU,MAAM;GACvB;EACD,MAAM,gBAAmC;GACxC,QAAQ;GACR,SAAS;GACT,aAAa,UAAU;GACvB,SAAS,UAAU;GACnB,QAAQ,UAAU;GAClB,OAAO,UAAU,MAAM;GACvB;EAED,MAAM,UAAU,iBAAiB,QAAQ,SAAS,KAAK,eAAe;EACtE,MAAM,OAAO,iBAAiB,QAAQ,MAAM,KAAK,YAAY;EAC7D,MAAM,OAAO,iBAAiB,QAAQ,MAAM,KAAK,YAAY;EAC7D,MAAM,SAAS,iBAAiB,QAAQ,QAAQ,KAAK,cAAc;AAEnE,SAAO;GACN;GACA;GACA;GACA;GACA,aAAa,UAAU;GACvB,WAAW,UAAU;GACrB;GACA,aAAa,UAAU;GACvB,SAAS,UAAU;GACnB,eAAe,UAAU;GACzB,YAAY,UAAU;GACtB,YAAY,UAAU;GACtB,cAAc,UAAU;GACxB,UAAU,OAAe;AACxB,YAAQ,YAAY,MAAM;AAC1B,SAAK,YAAY,MAAM;AACvB,SAAK,YAAY,MAAM;AACvB,WAAO,YAAY,MAAM;;GAE1B;;CAGF,MAAM,QACL,QACA,UAA8B,EAAE,EACO;EACvC,MAAM,UAAU,MAAM,kBAAkB,OAAO;AAC/C,MAAI,CAAC,WAAW,QAAQ,SAAS,WAAW,EAC3C,OAAM,IAAI,MACT,yDAAyD,SACzD;AAEF,SAAO,YAAY,oBAClB,QAAQ,UACR,QAAQ,YACR,QACA;;CAEF"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { o as AudioSignalComputeConfig, zt as FrameSignal } from "./index-
|
|
2
|
-
import { n as NodeRendererPlugin } from "./index-
|
|
1
|
+
import { o as AudioSignalComputeConfig, zt as FrameSignal } from "./index-ogL8Y-Ng.mjs";
|
|
2
|
+
import { n as NodeRendererPlugin } from "./index-DY3mLoPO.mjs";
|
|
3
3
|
|
|
4
4
|
//#region ../../nodes/node-modulate/dist/renderer.d.mts
|
|
5
5
|
//#region src/renderers/index.d.ts
|
|
@@ -34,4 +34,4 @@ declare const AudioSignal: {
|
|
|
34
34
|
};
|
|
35
35
|
//#endregion
|
|
36
36
|
export { _default as i, AudioSignalOptions as n, ExtractedAudioSignalsBundle as r, AudioSignal as t };
|
|
37
|
-
//# sourceMappingURL=audio-
|
|
37
|
+
//# sourceMappingURL=audio-nmU-BWxI.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audio-
|
|
1
|
+
{"version":3,"file":"audio-nmU-BWxI.d.mts","names":["_framefields_node_sdk_renderer0","_default","NodeRendererPlugin","Readonly","default"],"sources":["../../../nodes/node-modulate/dist/renderer.d.mts","../src/signals/audio.ts"],"sourcesContent":["import * as _framefields_node_sdk_renderer0 from \"@framefields/node-sdk/renderer\";\n\n//#region src/renderers/index.d.ts\ndeclare const _default: Readonly<_framefields_node_sdk_renderer0.NodeRendererPlugin>;\n//#endregion\nexport { _default as default };\n//# sourceMappingURL=renderer.d.mts.map"],"mappings":";;;;;cAGcC,UAAUE,SAASH;AAHiD;;;UCYjE,kBAAA,SAA2B;;EDT9BC,WAAsE,CAAA,EAAA,MAAA;;;UCenE,2BAAA;EANA,OAAA,EAOP,WAPO,CAAmB,MAAA,CAAA;EAMnB,IAAA,EAEV,WAFU,CAAA,MAAA,CAAA;EACP,IAAA,EAEH,WAFG,CAAA,MAAA,CAAA;EACH,MAAA,EAEE,WAFF,CAAA,MAAA,CAAA;EACA,WAAA,EAAA,MAAA;EACE,SAAA,EAAA,MAAA;EAAW,UAAA,EAAA,MAAA;EAaP,WAAA,EAAA,OAuGZ;EArGW,OAAA,EAAA,OAAA;EAED,aAAA,EAAA,OAAA;EACC,UAAA,EAAA,OAAA;EAAR,UAAA,EAAA,OAAA;EAoFO,YAAA,EAAA,OAAA;EACC,SAAA,CAAA,KAAA,EAAA,MAAA,CAAA,EAAA,IAAA;;AAAD,cA1FE,WA0FF,EAAA;gCAxFC,8CAED,qBACP,QAAQ;oCAoFD,qBACP,QAAQ"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Ht as updateClockSignals, Ot as isSignal, Pt as resolveMediaMimeType, bt as getActiveMediaMetadata, f as DEFAULT_DURATION_MS, wt as getMediaType } from "./dist-Bx6J0VcM.mjs";
|
|
2
2
|
import { i as webgpuRegistry, t as audioRegistry } from "./renderer-CtVkU_GA.mjs";
|
|
3
|
-
import { $ as registerHeadlessFont, A as drawLottieNode, B as inputStore, D as drawCaptionNode, M as drawSignalNode, N as drawSvgNode, O as drawGifNode, P as drawVideoNode, b as SlugGeometry, et as shaderStore, it as GetFontAssetUrl, j as drawParagraphNode, k as drawImageNode, ot as
|
|
3
|
+
import { $ as registerHeadlessFont, A as drawLottieNode, B as inputStore, D as drawCaptionNode, L as getHeadlessFontPath, M as drawSignalNode, N as drawSvgNode, O as drawGifNode, P as drawVideoNode, at as getEnv, b as SlugGeometry, et as shaderStore, it as GetFontAssetUrl, j as drawParagraphNode, k as drawImageNode, ot as resolveMediaSourceUrl, tt as signalRegistry, y as SlugFontCache } from "./dist-DgIKXWZ1.mjs";
|
|
4
4
|
import { AudioSampleSink } from "mediabunny";
|
|
5
5
|
|
|
6
6
|
//#region ../renderers/dist/index.mjs
|
|
@@ -691,6 +691,62 @@ function hslToRgb(h, s, l) {
|
|
|
691
691
|
hue2rgb(p, q, h - 1 / 3)
|
|
692
692
|
];
|
|
693
693
|
}
|
|
694
|
+
/**
|
|
695
|
+
* Preloads a font into the browser or headless environment.
|
|
696
|
+
*/
|
|
697
|
+
async function preloadFont(family, url) {
|
|
698
|
+
if (typeof window !== "undefined" && typeof FontFace !== "undefined") try {
|
|
699
|
+
const loadedFace = await new FontFace(family, `url(${url})`).load();
|
|
700
|
+
document.fonts.add(loadedFace);
|
|
701
|
+
await document.fonts.ready;
|
|
702
|
+
} catch (e) {
|
|
703
|
+
console.warn(`[preloadFont] Failed to load font "${family}" from ${url}:`, e);
|
|
704
|
+
}
|
|
705
|
+
else await registerHeadlessFont(family, url);
|
|
706
|
+
}
|
|
707
|
+
const EMOJI_REGEX = /\p{Extended_Pictographic}/gu;
|
|
708
|
+
let warnedNoEmojiFont = false;
|
|
709
|
+
/**
|
|
710
|
+
* Loads every font a composition's text uses (for layout and for drawing) and
|
|
711
|
+
* the emoji it draws. The browser preview and the export both call this, so
|
|
712
|
+
* they start each render from the same glyphs.
|
|
713
|
+
*/
|
|
714
|
+
async function preloadCompositionFonts(media, device, onError = (err, what) => console.warn(`[framefields] ${what}`, err)) {
|
|
715
|
+
const families = /* @__PURE__ */ new Set();
|
|
716
|
+
const emoji = /* @__PURE__ */ new Map();
|
|
717
|
+
(function walk(node) {
|
|
718
|
+
const op = node.operation;
|
|
719
|
+
if (op) {
|
|
720
|
+
const isText = op.op === "text" || op.kind === "text" || op.op === "caption" || op.kind === "caption" || typeof op.text === "string";
|
|
721
|
+
const family = op.fontFamily || (isText ? "Inter" : "");
|
|
722
|
+
if (family) families.add(family);
|
|
723
|
+
if (op.op === "text" && typeof op.text === "string") {
|
|
724
|
+
const fontSize = op.fontSize ?? 48;
|
|
725
|
+
for (const char of op.text.match(EMOJI_REGEX) ?? []) emoji.set(`${char}-${fontSize}`, fontSize);
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
for (const child of node.children ?? []) walk(child);
|
|
729
|
+
})(media);
|
|
730
|
+
const tasks = [];
|
|
731
|
+
for (const family of families) {
|
|
732
|
+
const url = getHeadlessFontPath(family) ?? GetFontAssetUrl(family);
|
|
733
|
+
tasks.push(preloadFont(family, url).catch((err) => onError(err, `font "${family}" from ${url}`)));
|
|
734
|
+
if (device) tasks.push(SlugFontCache.preloadSlugFont(device, family, url).catch((err) => onError(err, `Slug font "${family}" from ${url}`)));
|
|
735
|
+
}
|
|
736
|
+
if (device && emoji.size > 0) {
|
|
737
|
+
if (!SlugFontCache.hasEmojiFont() && !warnedNoEmojiFont) {
|
|
738
|
+
warnedNoEmojiFont = true;
|
|
739
|
+
console.warn("[framefields] Text uses emoji but no emoji font is registered, so exports draw nothing for them. Register a color emoji font (e.g. Noto Color Emoji) with FontManager.registerEmojiFont(path).");
|
|
740
|
+
}
|
|
741
|
+
for (const [key, fontSize] of emoji) {
|
|
742
|
+
const char = key.slice(0, key.lastIndexOf("-"));
|
|
743
|
+
tasks.push(SlugFontCache.preloadEmoji(device, char, fontSize).catch((err) => {
|
|
744
|
+
if (SlugFontCache.hasEmojiFont()) onError(err, `emoji "${char}"`);
|
|
745
|
+
}));
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
await Promise.all(tasks);
|
|
749
|
+
}
|
|
694
750
|
const defaultSpringConfig = {
|
|
695
751
|
damping: 10,
|
|
696
752
|
mass: 1,
|
|
@@ -910,19 +966,6 @@ function measureTextAsync(text, style) {
|
|
|
910
966
|
const fontUrl = GetFontAssetUrl(fontFamily);
|
|
911
967
|
return SlugFontCache.preloadSlugFont(null, fontFamily, fontUrl).then(() => measureText(text, style)).catch(() => measureText(text, style));
|
|
912
968
|
}
|
|
913
|
-
/**
|
|
914
|
-
* Preloads a font into the browser or headless environment.
|
|
915
|
-
*/
|
|
916
|
-
async function preloadFont(family, url) {
|
|
917
|
-
if (typeof window !== "undefined" && typeof FontFace !== "undefined") try {
|
|
918
|
-
const loadedFace = await new FontFace(family, `url(${url})`).load();
|
|
919
|
-
document.fonts.add(loadedFace);
|
|
920
|
-
await document.fonts.ready;
|
|
921
|
-
} catch (e) {
|
|
922
|
-
console.warn(`[preloadFont] Failed to load font "${family}" from ${url}:`, e);
|
|
923
|
-
}
|
|
924
|
-
else await registerHeadlessFont(family, url);
|
|
925
|
-
}
|
|
926
969
|
|
|
927
970
|
//#endregion
|
|
928
971
|
//#region ../compositions/dist/layer-logic-DBs4NHoi.mjs
|
|
@@ -1855,5 +1898,5 @@ async function mixAudioTracks(virtualMedia, fps, targetSampleRate = 48e3, device
|
|
|
1855
1898
|
}
|
|
1856
1899
|
|
|
1857
1900
|
//#endregion
|
|
1858
|
-
export { springCalculation as S, measureTextAsync as _, resolveLayerDuration as a,
|
|
1859
|
-
//# sourceMappingURL=dist-
|
|
1901
|
+
export { springCalculation as C, spring as S, measureTextAsync as _, resolveLayerDuration as a, preloadFont as b, MAX_LIGHTS as c, bezier as d, computeStrokeRect as f, measureText as g, measureSpring as h, mixAudioTracks as i, Matrix4Math as l, mapBlendModeToSkia as m, decodeAudioSource as n, Easing as o, interpolate as p, drawCompositionTree as r, LIGHT_UNIFORM_SIZE_BYTES as s, compositionStateStore as t, Vector3Math as u, parseColorToRgb as v, requiresIsolation as x, preloadCompositionFonts as y };
|
|
1902
|
+
//# sourceMappingURL=dist-55MP0aN4.mjs.map
|