@basementstudio/shader-lab 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (261) hide show
  1. package/.biome/plugins/README.md +21 -0
  2. package/.biome/plugins/no-anchor-element.grit +12 -0
  3. package/.biome/plugins/no-relative-parent-imports.grit +10 -0
  4. package/.biome/plugins/no-unnecessary-forwardref.grit +9 -0
  5. package/.changeset/README.md +17 -0
  6. package/.changeset/config.json +11 -0
  7. package/.editorconfig +40 -0
  8. package/.env.example +81 -0
  9. package/.gitattributes +19 -0
  10. package/.github/workflows/canary.yml +80 -0
  11. package/.github/workflows/ci.yml +37 -0
  12. package/.github/workflows/release.yml +56 -0
  13. package/.tldrignore +84 -0
  14. package/.vscode/extensions.json +20 -0
  15. package/.vscode/settings.json +105 -0
  16. package/README.md +119 -0
  17. package/biome.json +249 -0
  18. package/bun.lock +1224 -0
  19. package/next.config.ts +131 -0
  20. package/package.json +73 -0
  21. package/packages/shader-lab-react/CHANGELOG.md +9 -0
  22. package/packages/shader-lab-react/README.md +119 -0
  23. package/packages/shader-lab-react/assets/patterns/bars/1.svg +3 -0
  24. package/packages/shader-lab-react/assets/patterns/bars/2.svg +3 -0
  25. package/packages/shader-lab-react/assets/patterns/bars/3.svg +3 -0
  26. package/packages/shader-lab-react/assets/patterns/bars/4.svg +3 -0
  27. package/packages/shader-lab-react/assets/patterns/bars/5.svg +3 -0
  28. package/packages/shader-lab-react/assets/patterns/bars/6.svg +3 -0
  29. package/packages/shader-lab-react/assets/patterns/candles/1.svg +3 -0
  30. package/packages/shader-lab-react/assets/patterns/candles/2.svg +3 -0
  31. package/packages/shader-lab-react/assets/patterns/candles/3.svg +3 -0
  32. package/packages/shader-lab-react/assets/patterns/candles/4.svg +3 -0
  33. package/packages/shader-lab-react/assets/patterns/shapes/1.svg +3 -0
  34. package/packages/shader-lab-react/assets/patterns/shapes/2.svg +3 -0
  35. package/packages/shader-lab-react/assets/patterns/shapes/3.svg +3 -0
  36. package/packages/shader-lab-react/assets/patterns/shapes/4.svg +4 -0
  37. package/packages/shader-lab-react/assets/patterns/shapes/5.svg +3 -0
  38. package/packages/shader-lab-react/assets/patterns/shapes/6.svg +4 -0
  39. package/packages/shader-lab-react/assets/textures/blue-noise.png +0 -0
  40. package/packages/shader-lab-react/package.json +36 -0
  41. package/packages/shader-lab-react/scripts/fix-esm-specifiers.mjs +57 -0
  42. package/packages/shader-lab-react/scripts/prepare-dist.mjs +4 -0
  43. package/packages/shader-lab-react/src/ambient/three-tsl.d.ts +146 -0
  44. package/packages/shader-lab-react/src/ambient/three-webgpu.d.ts +51 -0
  45. package/packages/shader-lab-react/src/easings.ts +4 -0
  46. package/packages/shader-lab-react/src/index.ts +35 -0
  47. package/packages/shader-lab-react/src/lib/editor/custom-shader/shared.ts +2 -0
  48. package/packages/shader-lab-react/src/renderer/ascii-atlas.ts +83 -0
  49. package/packages/shader-lab-react/src/renderer/ascii-pass.ts +416 -0
  50. package/packages/shader-lab-react/src/renderer/asset-url.ts +3 -0
  51. package/packages/shader-lab-react/src/renderer/blend-modes.ts +229 -0
  52. package/packages/shader-lab-react/src/renderer/contracts.ts +54 -0
  53. package/packages/shader-lab-react/src/renderer/create-webgpu-renderer.ts +48 -0
  54. package/packages/shader-lab-react/src/renderer/crt-pass.ts +1040 -0
  55. package/packages/shader-lab-react/src/renderer/custom-shader-pass.ts +108 -0
  56. package/packages/shader-lab-react/src/renderer/custom-shader-runtime.ts +309 -0
  57. package/packages/shader-lab-react/src/renderer/dither-textures.ts +99 -0
  58. package/packages/shader-lab-react/src/renderer/dithering-pass.ts +322 -0
  59. package/packages/shader-lab-react/src/renderer/gradient-pass.ts +521 -0
  60. package/packages/shader-lab-react/src/renderer/halftone-pass.ts +932 -0
  61. package/packages/shader-lab-react/src/renderer/ink-pass.ts +802 -0
  62. package/packages/shader-lab-react/src/renderer/live-pass.ts +194 -0
  63. package/packages/shader-lab-react/src/renderer/media-pass.ts +187 -0
  64. package/packages/shader-lab-react/src/renderer/media-texture.ts +66 -0
  65. package/packages/shader-lab-react/src/renderer/particle-grid-pass.ts +389 -0
  66. package/packages/shader-lab-react/src/renderer/pass-node.ts +209 -0
  67. package/packages/shader-lab-react/src/renderer/pattern-atlas.ts +133 -0
  68. package/packages/shader-lab-react/src/renderer/pattern-pass.ts +552 -0
  69. package/packages/shader-lab-react/src/renderer/pipeline-manager.ts +369 -0
  70. package/packages/shader-lab-react/src/renderer/pixel-sorting-pass.ts +277 -0
  71. package/packages/shader-lab-react/src/renderer/shaders/tsl/color/tonemapping.ts +87 -0
  72. package/packages/shader-lab-react/src/renderer/shaders/tsl/cosine-palette.ts +9 -0
  73. package/packages/shader-lab-react/src/renderer/shaders/tsl/noise/common.ts +31 -0
  74. package/packages/shader-lab-react/src/renderer/shaders/tsl/noise/curl-noise-3d.ts +36 -0
  75. package/packages/shader-lab-react/src/renderer/shaders/tsl/noise/curl-noise-4d.ts +36 -0
  76. package/packages/shader-lab-react/src/renderer/shaders/tsl/noise/fbm.ts +13 -0
  77. package/packages/shader-lab-react/src/renderer/shaders/tsl/noise/perlin-noise-3d.ts +96 -0
  78. package/packages/shader-lab-react/src/renderer/shaders/tsl/noise/ridge-noise.ts +24 -0
  79. package/packages/shader-lab-react/src/renderer/shaders/tsl/noise/simplex-noise-3d.ts +79 -0
  80. package/packages/shader-lab-react/src/renderer/shaders/tsl/noise/simplex-noise-4d.ts +89 -0
  81. package/packages/shader-lab-react/src/renderer/shaders/tsl/noise/turbulence.ts +56 -0
  82. package/packages/shader-lab-react/src/renderer/shaders/tsl/noise/value-noise-3d.ts +32 -0
  83. package/packages/shader-lab-react/src/renderer/shaders/tsl/noise/voronoi-noise-3d.ts +60 -0
  84. package/packages/shader-lab-react/src/renderer/shaders/tsl/patterns/bloom-edge-pattern.ts +15 -0
  85. package/packages/shader-lab-react/src/renderer/shaders/tsl/patterns/bloom.ts +11 -0
  86. package/packages/shader-lab-react/src/renderer/shaders/tsl/patterns/canvas-weave-pattern.ts +24 -0
  87. package/packages/shader-lab-react/src/renderer/shaders/tsl/patterns/grain-texture-pattern.ts +9 -0
  88. package/packages/shader-lab-react/src/renderer/shaders/tsl/patterns/repeating-pattern.ts +11 -0
  89. package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/atan2.ts +9 -0
  90. package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/complex-conj.ts +9 -0
  91. package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/complex-cos.ts +10 -0
  92. package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/complex-div.ts +11 -0
  93. package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/complex-log.ts +7 -0
  94. package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/complex-mobius.ts +12 -0
  95. package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/complex-mul.ts +9 -0
  96. package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/complex-pow.ts +16 -0
  97. package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/complex-sin.ts +10 -0
  98. package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/complex-sqrt.ts +18 -0
  99. package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/complex-tan.ts +12 -0
  100. package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/complex-to-polar.ts +10 -0
  101. package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/hyperbolic.ts +20 -0
  102. package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/index.ts +48 -0
  103. package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/rotate.ts +15 -0
  104. package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/screen-aspect-uv.ts +15 -0
  105. package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/sd-box-2d.ts +6 -0
  106. package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/sd-diamond.ts +6 -0
  107. package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/sd-rhombus.ts +27 -0
  108. package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/sd-sphere.ts +6 -0
  109. package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/smax.ts +7 -0
  110. package/packages/shader-lab-react/src/renderer/shaders/tsl/utils/smin.ts +7 -0
  111. package/packages/shader-lab-react/src/renderer/text-pass.ts +176 -0
  112. package/packages/shader-lab-react/src/runtime-clock.ts +42 -0
  113. package/packages/shader-lab-react/src/runtime-frame.ts +29 -0
  114. package/packages/shader-lab-react/src/shader-lab-composition.tsx +163 -0
  115. package/packages/shader-lab-react/src/timeline.ts +283 -0
  116. package/packages/shader-lab-react/src/types/editor.ts +5 -0
  117. package/packages/shader-lab-react/src/types.ts +141 -0
  118. package/packages/shader-lab-react/tsconfig.build.json +8 -0
  119. package/packages/shader-lab-react/tsconfig.json +21 -0
  120. package/postcss.config.mjs +5 -0
  121. package/public/assets/fonts/msdf/geist-mono/GeistMono-Regular-msdf-atlas.png +0 -0
  122. package/public/assets/fonts/msdf/geist-mono/GeistMono-Regular-msdf.json +1412 -0
  123. package/public/assets/patterns/bars/1.svg +3 -0
  124. package/public/assets/patterns/bars/2.svg +3 -0
  125. package/public/assets/patterns/bars/3.svg +3 -0
  126. package/public/assets/patterns/bars/4.svg +3 -0
  127. package/public/assets/patterns/bars/5.svg +3 -0
  128. package/public/assets/patterns/bars/6.svg +3 -0
  129. package/public/assets/patterns/candles/1.svg +3 -0
  130. package/public/assets/patterns/candles/2.svg +3 -0
  131. package/public/assets/patterns/candles/3.svg +3 -0
  132. package/public/assets/patterns/candles/4.svg +3 -0
  133. package/public/assets/patterns/shapes/1.svg +3 -0
  134. package/public/assets/patterns/shapes/2.svg +3 -0
  135. package/public/assets/patterns/shapes/3.svg +3 -0
  136. package/public/assets/patterns/shapes/4.svg +4 -0
  137. package/public/assets/patterns/shapes/5.svg +3 -0
  138. package/public/assets/patterns/shapes/6.svg +4 -0
  139. package/public/fonts/geist/Geist-Mono.woff2 +0 -0
  140. package/public/textures/blue-noise.png +0 -0
  141. package/public/textures/crt-mask.png +0 -0
  142. package/src/app/design/page.tsx +398 -0
  143. package/src/app/favicon.ico +0 -0
  144. package/src/app/globals.css +280 -0
  145. package/src/app/layout.tsx +89 -0
  146. package/src/app/page.tsx +20 -0
  147. package/src/app/robots.ts +13 -0
  148. package/src/app/sitemap.ts +13 -0
  149. package/src/components/editor/editor-canvas-viewport.tsx +116 -0
  150. package/src/components/editor/editor-export-dialog.tsx +1177 -0
  151. package/src/components/editor/editor-timeline-overlay.tsx +983 -0
  152. package/src/components/editor/editor-topbar.tsx +287 -0
  153. package/src/components/editor/layer-sidebar.tsx +738 -0
  154. package/src/components/editor/properties-sidebar-content.tsx +574 -0
  155. package/src/components/editor/properties-sidebar-fields.tsx +389 -0
  156. package/src/components/editor/properties-sidebar-utils.ts +178 -0
  157. package/src/components/editor/properties-sidebar.tsx +421 -0
  158. package/src/components/ui/button/index.tsx +57 -0
  159. package/src/components/ui/color-picker/index.tsx +358 -0
  160. package/src/components/ui/glass-panel/index.tsx +45 -0
  161. package/src/components/ui/icon-button/index.tsx +46 -0
  162. package/src/components/ui/select/index.tsx +136 -0
  163. package/src/components/ui/slider/index.tsx +192 -0
  164. package/src/components/ui/toggle/index.tsx +34 -0
  165. package/src/components/ui/typography/index.tsx +61 -0
  166. package/src/components/ui/xy-pad/index.tsx +160 -0
  167. package/src/features/editor/components/editor-export-dialog.module.css +271 -0
  168. package/src/hooks/use-editor-renderer.ts +182 -0
  169. package/src/lib/app.ts +6 -0
  170. package/src/lib/cn.ts +7 -0
  171. package/src/lib/easings.ts +240 -0
  172. package/src/lib/editor/config/layer-registry.ts +2434 -0
  173. package/src/lib/editor/custom-shader/shared.ts +28 -0
  174. package/src/lib/editor/export.ts +420 -0
  175. package/src/lib/editor/history.ts +71 -0
  176. package/src/lib/editor/layers.ts +76 -0
  177. package/src/lib/editor/parameter-schema.ts +75 -0
  178. package/src/lib/editor/project-file.ts +145 -0
  179. package/src/lib/editor/shader-export-snippet.ts +37 -0
  180. package/src/lib/editor/shader-export.ts +315 -0
  181. package/src/lib/editor/timeline/evaluate.ts +252 -0
  182. package/src/lib/editor/view-transform.ts +58 -0
  183. package/src/lib/fonts.ts +28 -0
  184. package/src/renderer/ascii-atlas.ts +83 -0
  185. package/src/renderer/ascii-pass.ts +416 -0
  186. package/src/renderer/blend-modes.ts +229 -0
  187. package/src/renderer/contracts.ts +161 -0
  188. package/src/renderer/create-webgpu-renderer.ts +48 -0
  189. package/src/renderer/crt-pass.ts +1040 -0
  190. package/src/renderer/custom-shader-pass.ts +117 -0
  191. package/src/renderer/custom-shader-runtime.ts +309 -0
  192. package/src/renderer/dither-textures.ts +99 -0
  193. package/src/renderer/dithering-pass.ts +322 -0
  194. package/src/renderer/gradient-pass.ts +520 -0
  195. package/src/renderer/halftone-pass.ts +932 -0
  196. package/src/renderer/ink-pass.ts +683 -0
  197. package/src/renderer/live-pass.ts +194 -0
  198. package/src/renderer/media-pass.ts +187 -0
  199. package/src/renderer/media-texture.ts +66 -0
  200. package/src/renderer/particle-grid-pass.ts +389 -0
  201. package/src/renderer/pass-node-factory.ts +33 -0
  202. package/src/renderer/pass-node.ts +209 -0
  203. package/src/renderer/pattern-atlas.ts +97 -0
  204. package/src/renderer/pattern-pass.ts +552 -0
  205. package/src/renderer/pipeline-manager.ts +343 -0
  206. package/src/renderer/pixel-sorting-pass.ts +277 -0
  207. package/src/renderer/project-clock.ts +57 -0
  208. package/src/renderer/shaders/tsl/color/tonemapping.ts +86 -0
  209. package/src/renderer/shaders/tsl/cosine-palette.ts +8 -0
  210. package/src/renderer/shaders/tsl/noise/common.ts +30 -0
  211. package/src/renderer/shaders/tsl/noise/curl-noise-3d.ts +35 -0
  212. package/src/renderer/shaders/tsl/noise/curl-noise-4d.ts +35 -0
  213. package/src/renderer/shaders/tsl/noise/fbm.ts +12 -0
  214. package/src/renderer/shaders/tsl/noise/perlin-noise-3d.ts +97 -0
  215. package/src/renderer/shaders/tsl/noise/ridge-noise.ts +23 -0
  216. package/src/renderer/shaders/tsl/noise/simplex-noise-3d.ts +78 -0
  217. package/src/renderer/shaders/tsl/noise/simplex-noise-4d.ts +88 -0
  218. package/src/renderer/shaders/tsl/noise/turbulence.ts +55 -0
  219. package/src/renderer/shaders/tsl/noise/value-noise-3d.ts +31 -0
  220. package/src/renderer/shaders/tsl/noise/voronoi-noise-3d.ts +59 -0
  221. package/src/renderer/shaders/tsl/patterns/bloom-edge-pattern.ts +14 -0
  222. package/src/renderer/shaders/tsl/patterns/bloom.ts +10 -0
  223. package/src/renderer/shaders/tsl/patterns/canvas-weave-pattern.ts +23 -0
  224. package/src/renderer/shaders/tsl/patterns/grain-texture-pattern.ts +8 -0
  225. package/src/renderer/shaders/tsl/patterns/repeating-pattern.ts +10 -0
  226. package/src/renderer/shaders/tsl/utils/atan2.ts +8 -0
  227. package/src/renderer/shaders/tsl/utils/complex-conj.ts +8 -0
  228. package/src/renderer/shaders/tsl/utils/complex-cos.ts +9 -0
  229. package/src/renderer/shaders/tsl/utils/complex-div.ts +10 -0
  230. package/src/renderer/shaders/tsl/utils/complex-log.ts +6 -0
  231. package/src/renderer/shaders/tsl/utils/complex-mobius.ts +11 -0
  232. package/src/renderer/shaders/tsl/utils/complex-mul.ts +8 -0
  233. package/src/renderer/shaders/tsl/utils/complex-pow.ts +15 -0
  234. package/src/renderer/shaders/tsl/utils/complex-sin.ts +9 -0
  235. package/src/renderer/shaders/tsl/utils/complex-sqrt.ts +17 -0
  236. package/src/renderer/shaders/tsl/utils/complex-tan.ts +11 -0
  237. package/src/renderer/shaders/tsl/utils/complex-to-polar.ts +9 -0
  238. package/src/renderer/shaders/tsl/utils/hyperbolic.ts +19 -0
  239. package/src/renderer/shaders/tsl/utils/index.ts +47 -0
  240. package/src/renderer/shaders/tsl/utils/rotate.ts +14 -0
  241. package/src/renderer/shaders/tsl/utils/screen-aspect-uv.ts +14 -0
  242. package/src/renderer/shaders/tsl/utils/sd-box-2d.ts +5 -0
  243. package/src/renderer/shaders/tsl/utils/sd-diamond.ts +5 -0
  244. package/src/renderer/shaders/tsl/utils/sd-rhombus.ts +26 -0
  245. package/src/renderer/shaders/tsl/utils/sd-sphere.ts +5 -0
  246. package/src/renderer/shaders/tsl/utils/smax.ts +7 -0
  247. package/src/renderer/shaders/tsl/utils/smin.ts +6 -0
  248. package/src/renderer/text-pass.ts +176 -0
  249. package/src/store/asset-store.ts +193 -0
  250. package/src/store/editor-store.ts +223 -0
  251. package/src/store/history-store.ts +172 -0
  252. package/src/store/index.ts +31 -0
  253. package/src/store/layer-store.ts +675 -0
  254. package/src/store/timeline-store.ts +572 -0
  255. package/src/types/assets.d.ts +6 -0
  256. package/src/types/css.d.ts +21 -0
  257. package/src/types/editor.ts +357 -0
  258. package/src/types/react.d.ts +15 -0
  259. package/src/types/three-tsl.d.ts +146 -0
  260. package/src/types/three-webgpu.d.ts +51 -0
  261. package/tsconfig.json +49 -0
@@ -0,0 +1,271 @@
1
+ .dialogRoot {
2
+ inset: 0;
3
+ position: fixed;
4
+ z-index: 90;
5
+ }
6
+
7
+ .backdrop {
8
+ background: rgb(4 5 7 / 0.56);
9
+ border: 0;
10
+ inset: 0;
11
+ position: absolute;
12
+ width: 100%;
13
+ }
14
+
15
+ .dialog {
16
+ max-height: calc(100vh - 112px);
17
+ overflow: hidden;
18
+ padding: 0;
19
+ }
20
+
21
+ .dialogWrap {
22
+ inset: 76px auto auto 50%;
23
+ max-width: min(560px, calc(100vw - 32px));
24
+ position: absolute;
25
+ transform: translateX(-50%);
26
+ width: min(560px, calc(100vw - 32px));
27
+ }
28
+
29
+ .dialogMotion {
30
+ width: 100%;
31
+ }
32
+
33
+ .dialogHeader {
34
+ align-items: center;
35
+ border-bottom: 1px solid var(--ds-border-divider);
36
+ display: flex;
37
+ justify-content: space-between;
38
+ padding: 14px 16px 12px;
39
+ }
40
+
41
+ .dialogTitle {
42
+ line-height: 20px;
43
+ }
44
+
45
+ .closeButton {
46
+ height: 28px;
47
+ width: 28px;
48
+ }
49
+
50
+ .tabRow {
51
+ border-bottom: 1px solid var(--ds-border-divider);
52
+ display: flex;
53
+ gap: 6px;
54
+ padding: 10px 16px;
55
+ }
56
+
57
+ .tabButton {
58
+ align-items: center;
59
+ background-color: transparent;
60
+ border: 1px solid transparent;
61
+ border-radius: var(--ds-radius-control);
62
+ display: inline-flex;
63
+ justify-content: center;
64
+ line-height: 1;
65
+ min-height: 28px;
66
+ padding: 0 10px;
67
+ transition:
68
+ background-color 160ms var(--ease-out-cubic),
69
+ border-color 160ms var(--ease-out-cubic),
70
+ color 160ms var(--ease-out-cubic);
71
+ }
72
+
73
+ .tabButton:hover {
74
+ background-color: var(--ds-color-surface-subtle);
75
+ border-color: var(--ds-border-subtle);
76
+ }
77
+
78
+ .tabButtonActive {
79
+ background-color: var(--ds-color-surface-active);
80
+ border-color: var(--ds-border-active);
81
+ }
82
+
83
+ .dialogBody {
84
+ overflow: hidden;
85
+ padding: 14px 16px 16px;
86
+ }
87
+
88
+ .contentViewport {
89
+ position: relative;
90
+ }
91
+
92
+ .measureWrap {
93
+ inset: 0 auto auto 0;
94
+ pointer-events: none;
95
+ position: absolute;
96
+ visibility: hidden;
97
+ width: 100%;
98
+ z-index: -1;
99
+ }
100
+
101
+ .measureView {
102
+ width: 100%;
103
+ }
104
+
105
+ .tabPane {
106
+ width: 100%;
107
+ }
108
+
109
+ .sectionStack {
110
+ display: flex;
111
+ flex-direction: column;
112
+ gap: 14px;
113
+ }
114
+
115
+ .field {
116
+ display: flex;
117
+ flex-direction: column;
118
+ gap: 8px;
119
+ }
120
+
121
+ .fieldLabel {
122
+ text-transform: uppercase;
123
+ }
124
+
125
+ .presetRow {
126
+ display: flex;
127
+ flex-wrap: wrap;
128
+ gap: 6px;
129
+ }
130
+
131
+ .pillButton {
132
+ align-items: center;
133
+ background-color: var(--ds-color-surface-control);
134
+ border: 1px solid var(--ds-border-divider);
135
+ border-radius: var(--ds-radius-control);
136
+ display: inline-flex;
137
+ justify-content: center;
138
+ line-height: 1;
139
+ min-height: 28px;
140
+ padding: 0 10px;
141
+ transition:
142
+ background-color 160ms var(--ease-out-cubic),
143
+ border-color 160ms var(--ease-out-cubic),
144
+ color 160ms var(--ease-out-cubic);
145
+ }
146
+
147
+ .tabButton :global(.type-label),
148
+ .pillButton :global(.type-label) {
149
+ line-height: 1;
150
+ }
151
+
152
+ .pillButton:hover:not(:disabled) {
153
+ background-color: rgb(255 255 255 / 0.08);
154
+ border-color: var(--ds-border-hover);
155
+ }
156
+
157
+ .pillButtonActive {
158
+ background-color: var(--ds-color-surface-active);
159
+ border-color: var(--ds-border-active);
160
+ }
161
+
162
+ .pillButton:disabled {
163
+ cursor: not-allowed;
164
+ opacity: 0.42;
165
+ }
166
+
167
+ .inlineGrid {
168
+ display: grid;
169
+ gap: 10px;
170
+ grid-template-columns: repeat(2, minmax(0, 1fr));
171
+ }
172
+
173
+ .numberInput {
174
+ background-color: var(--ds-color-surface-control);
175
+ border: 1px solid var(--ds-border-divider);
176
+ border-radius: var(--ds-radius-control);
177
+ color: var(--ds-color-text-primary);
178
+ font-family: var(--ds-font-mono);
179
+ font-size: 12px;
180
+ line-height: 16px;
181
+ min-height: 36px;
182
+ padding: 8px 10px;
183
+ }
184
+
185
+ .note {
186
+ line-height: 14px;
187
+ }
188
+
189
+ .inlineCode {
190
+ background-color: rgb(255 255 255 / 0.06);
191
+ border: 1px solid rgb(255 255 255 / 0.09);
192
+ border-radius: 6px;
193
+ font-family: var(--ds-font-mono);
194
+ font-size: 11px;
195
+ padding: 1px 5px;
196
+ }
197
+
198
+ .issueList {
199
+ background-color: rgb(255 74 74 / 0.06);
200
+ border: 1px solid rgb(255 74 74 / 0.14);
201
+ border-radius: var(--ds-radius-panel);
202
+ display: flex;
203
+ flex-direction: column;
204
+ gap: 8px;
205
+ padding: 12px;
206
+ }
207
+
208
+ .codeBlock {
209
+ background-color: rgb(255 255 255 / 0.04);
210
+ border: 1px solid var(--ds-border-divider);
211
+ border-radius: var(--ds-radius-panel);
212
+ font-family: var(--ds-font-mono);
213
+ font-size: 11px;
214
+ line-height: 1.55;
215
+ margin: 0;
216
+ max-height: 280px;
217
+ overflow: auto;
218
+ padding: 12px;
219
+ white-space: pre-wrap;
220
+ word-break: break-word;
221
+ }
222
+
223
+ .dropZone {
224
+ align-items: center;
225
+ background-color: var(--ds-color-surface-subtle);
226
+ border: 1px dashed var(--ds-border-divider);
227
+ border-radius: var(--ds-radius-panel);
228
+ display: grid;
229
+ gap: 12px;
230
+ grid-template-columns: auto 1fr auto;
231
+ padding: 14px;
232
+ }
233
+
234
+ .dropZoneActive {
235
+ background-color: var(--ds-color-surface-active);
236
+ border-color: var(--ds-border-hover);
237
+ }
238
+
239
+ .dropTitle {
240
+ line-height: 16px;
241
+ }
242
+
243
+ .dropText {
244
+ margin-top: 4px;
245
+ }
246
+
247
+ .errorMessage,
248
+ .statusMessage {
249
+ border-radius: var(--ds-radius-control);
250
+ line-height: 14px;
251
+ margin: 0 16px 16px;
252
+ padding: 10px 12px;
253
+ }
254
+
255
+ .errorMessage {
256
+ background-color: rgb(255 74 74 / 0.08);
257
+ border: 1px solid rgb(255 74 74 / 0.18);
258
+ color: rgb(255 191 191 / 0.92);
259
+ }
260
+
261
+ .statusMessage {
262
+ background-color: rgb(255 255 255 / 0.06);
263
+ border: 1px solid rgb(255 255 255 / 0.09);
264
+ }
265
+
266
+ @media (width < 900px) {
267
+ .inlineGrid,
268
+ .dropZone {
269
+ grid-template-columns: 1fr;
270
+ }
271
+ }
@@ -0,0 +1,182 @@
1
+ "use client"
2
+
3
+ import { useEffect, useRef, useState } from "react"
4
+ import {
5
+ buildRendererFrame,
6
+ type EditorRenderer,
7
+ } from "@/renderer/contracts"
8
+ import {
9
+ browserSupportsWebGPU,
10
+ createWebGPURenderer,
11
+ } from "@/renderer/create-webgpu-renderer"
12
+ import type { Size } from "@/types/editor"
13
+ import { useAssetStore } from "@/store/asset-store"
14
+ import { useEditorStore } from "@/store/editor-store"
15
+ import { useLayerStore } from "@/store/layer-store"
16
+ import { useTimelineStore } from "@/store/timeline-store"
17
+
18
+ function getPixelRatio(): number {
19
+ if (typeof window === "undefined") {
20
+ return 1
21
+ }
22
+
23
+ return Math.min(window.devicePixelRatio || 1, 2)
24
+ }
25
+
26
+ function measureElement(element: HTMLElement): Size {
27
+ const bounds = element.getBoundingClientRect()
28
+
29
+ return {
30
+ height: Math.max(1, Math.round(bounds.height)),
31
+ width: Math.max(1, Math.round(bounds.width)),
32
+ }
33
+ }
34
+
35
+ export function useEditorRenderer() {
36
+ const canvasRef = useRef<HTMLCanvasElement | null>(null)
37
+ const viewportRef = useRef<HTMLDivElement | null>(null)
38
+ const rendererRef = useRef<EditorRenderer | null>(null)
39
+ const animationFrameRef = useRef<number | null>(null)
40
+ const [isReady, setIsReady] = useState(false)
41
+ const [fallbackMessage, setFallbackMessage] = useState<string | null>(null)
42
+
43
+ useEffect(() => {
44
+ const canvas = canvasRef.current
45
+ const viewport = viewportRef.current
46
+
47
+ if (!(canvas && viewport)) {
48
+ return
49
+ }
50
+
51
+ const canvasElement = canvas
52
+ const viewportElement = viewport
53
+
54
+ const editorStore = useEditorStore.getState()
55
+
56
+ if (!browserSupportsWebGPU()) {
57
+ editorStore.setWebGPUStatus(
58
+ "unsupported",
59
+ "This browser does not expose WebGPU yet.",
60
+ )
61
+ setFallbackMessage("WebGPU is not available in this browser.")
62
+ return
63
+ }
64
+
65
+ let isDisposed = false
66
+ let lastFrameTime = performance.now()
67
+ let previewTime = 0
68
+ let resizeObserver: ResizeObserver | null = null
69
+
70
+ editorStore.setWebGPUStatus("initializing")
71
+
72
+ async function initializeRenderer() {
73
+ try {
74
+ const renderer = await createWebGPURenderer(canvasElement)
75
+
76
+ if (isDisposed) {
77
+ renderer.dispose()
78
+ return
79
+ }
80
+
81
+ rendererRef.current = renderer
82
+ await renderer.initialize()
83
+
84
+ if (isDisposed) {
85
+ renderer.dispose()
86
+ return
87
+ }
88
+
89
+ const initialSize = measureElement(viewportElement)
90
+ editorStore.setCanvasSize(initialSize.width, initialSize.height)
91
+ renderer.resize(initialSize, getPixelRatio())
92
+ editorStore.setWebGPUStatus("ready")
93
+ setIsReady(true)
94
+
95
+ resizeObserver = new ResizeObserver(([entry]) => {
96
+ if (!entry) {
97
+ return
98
+ }
99
+
100
+ const nextSize = {
101
+ height: Math.max(1, Math.round(entry.contentRect.height)),
102
+ width: Math.max(1, Math.round(entry.contentRect.width)),
103
+ }
104
+
105
+ useEditorStore.getState().setCanvasSize(nextSize.width, nextSize.height)
106
+ renderer.resize(nextSize, getPixelRatio())
107
+ })
108
+
109
+ resizeObserver.observe(viewportElement)
110
+
111
+ const renderFrame = (now: number) => {
112
+ const layerState = useLayerStore.getState()
113
+ const assetState = useAssetStore.getState()
114
+ const editorState = useEditorStore.getState()
115
+ const delta = Math.max(0, (now - lastFrameTime) / 1000)
116
+
117
+ lastFrameTime = now
118
+ previewTime += delta
119
+
120
+ let timelineState = useTimelineStore.getState()
121
+
122
+ if (timelineState.isPlaying) {
123
+ timelineState.advance(delta)
124
+ timelineState = useTimelineStore.getState()
125
+ }
126
+
127
+ if (delta > 0) {
128
+ editorState.setFps(1 / delta)
129
+ }
130
+
131
+ const frame = buildRendererFrame({
132
+ assets: assetState.assets,
133
+ clockTime: timelineState.isPlaying ? timelineState.currentTime : previewTime,
134
+ delta,
135
+ layers: layerState.layers,
136
+ outputSize: editorState.outputSize,
137
+ pixelRatio: getPixelRatio(),
138
+ startupPreviewDismissed: editorState.startupPreviewDismissed,
139
+ timeline: timelineState,
140
+ viewportSize: editorState.canvasSize,
141
+ })
142
+
143
+ renderer.render(frame)
144
+ animationFrameRef.current = window.requestAnimationFrame(renderFrame)
145
+ }
146
+
147
+ animationFrameRef.current = window.requestAnimationFrame(renderFrame)
148
+ } catch (error) {
149
+ const message =
150
+ error instanceof Error ? error.message : "Renderer initialization failed."
151
+
152
+ useEditorStore.getState().setWebGPUStatus("error", message)
153
+ setFallbackMessage(message)
154
+ }
155
+ }
156
+
157
+ void initializeRenderer()
158
+
159
+ return () => {
160
+ isDisposed = true
161
+
162
+ if (resizeObserver) {
163
+ resizeObserver.disconnect()
164
+ }
165
+
166
+ if (animationFrameRef.current !== null) {
167
+ window.cancelAnimationFrame(animationFrameRef.current)
168
+ }
169
+
170
+ rendererRef.current?.dispose()
171
+ rendererRef.current = null
172
+ setIsReady(false)
173
+ }
174
+ }, [])
175
+
176
+ return {
177
+ canvasRef,
178
+ fallbackMessage,
179
+ isReady,
180
+ viewportRef,
181
+ }
182
+ }
package/src/lib/app.ts ADDED
@@ -0,0 +1,6 @@
1
+ export const APP_NAME = "ShaderLab"
2
+ export const APP_DEFAULT_TITLE = "Shader Lab"
3
+ export const APP_TITLE_TEMPLATE = "%s | Shader Lab"
4
+ export const APP_DESCRIPTION = "It's shading time"
5
+ export const APP_BASE_URL =
6
+ process.env.NEXT_PUBLIC_BASE_URL ?? "https://localhost:3000"
package/src/lib/cn.ts ADDED
@@ -0,0 +1,7 @@
1
+ import { cx } from "class-variance-authority"
2
+ import type { ClassValue } from "class-variance-authority/types"
3
+ import { twMerge } from "tailwind-merge"
4
+
5
+ export function cn(...inputs: ClassValue[]) {
6
+ return twMerge(cx(inputs))
7
+ }
@@ -0,0 +1,240 @@
1
+ /**
2
+ * Easing Functions
3
+ *
4
+ * A complete collection of easing curves for animations.
5
+ * All functions take a progress value (0-1) and return an eased value (0-1).
6
+ *
7
+ * @see https://easings.net for visual references
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * import { easings, type EasingName } from '@/utils/easings'
12
+ *
13
+ * // Direct usage
14
+ * const eased = easings.easeOutCubic(0.5)
15
+ *
16
+ * // With a variable easing name
17
+ * const easingName: EasingName = 'easeInOutQuart'
18
+ * const value = easings[easingName](progress)
19
+ * ```
20
+ *
21
+ * ## Choosing an Easing
22
+ *
23
+ * | Category | Use Case |
24
+ * |----------|----------|
25
+ * | `easeOut*` | UI feedback, appearing elements (most common) |
26
+ * | `easeIn*` | Exiting elements, building tension |
27
+ * | `easeInOut*` | State transitions, loading indicators |
28
+ * | `*Cubic/Quart` | Natural, balanced motion |
29
+ * | `*Expo` | Dramatic, snappy motion |
30
+ * | `*Elastic/Bounce` | Playful, attention-grabbing |
31
+ */
32
+
33
+ const pow = Math.pow
34
+ const sqrt = Math.sqrt
35
+ const sin = Math.sin
36
+ const cos = Math.cos
37
+ const PI = Math.PI
38
+
39
+ // Easing constants
40
+ const c1 = 1.70158
41
+ const c2 = c1 * 1.525
42
+ const c3 = c1 + 1
43
+ const c4 = (2 * PI) / 3
44
+ const c5 = (2 * PI) / 4.5
45
+
46
+ /** Bounce out helper (used by bounce easings) */
47
+ const bounceOut = (x: number): number => {
48
+ const n1 = 7.5625
49
+ const d1 = 2.75
50
+
51
+ if (x < 1 / d1) {
52
+ return n1 * x * x
53
+ }
54
+ if (x < 2 / d1) {
55
+ return n1 * (x - 1.5 / d1) * x + 0.75
56
+ }
57
+ if (x < 2.5 / d1) {
58
+ return n1 * (x - 2.25 / d1) * x + 0.9375
59
+ }
60
+ return n1 * (x - 2.625 / d1) * x + 0.984375
61
+ }
62
+
63
+ /**
64
+ * Collection of easing functions.
65
+ * Each function takes progress (0-1) and returns eased value (0-1).
66
+ */
67
+ export const easings = {
68
+ // ─────────────────────────────────────────────────────────────────────────────
69
+ // Linear (no easing)
70
+ // ─────────────────────────────────────────────────────────────────────────────
71
+
72
+ /** No easing - constant speed */
73
+ linear: (x: number): number => x,
74
+
75
+ // ─────────────────────────────────────────────────────────────────────────────
76
+ // Quadratic (power of 2)
77
+ // ─────────────────────────────────────────────────────────────────────────────
78
+
79
+ /** Slow start */
80
+ easeInQuad: (x: number): number => x * x,
81
+
82
+ /** Slow end */
83
+ easeOutQuad: (x: number): number => 1 - (1 - x) * (1 - x),
84
+
85
+ /** Slow start and end */
86
+ easeInOutQuad: (x: number): number =>
87
+ x < 0.5 ? 2 * x * x : 1 - pow(-2 * x + 2, 2) / 2,
88
+
89
+ // ─────────────────────────────────────────────────────────────────────────────
90
+ // Cubic (power of 3) - Most commonly used
91
+ // ─────────────────────────────────────────────────────────────────────────────
92
+
93
+ /** Slow start, natural feel */
94
+ easeInCubic: (x: number): number => x * x * x,
95
+
96
+ /** Slow end, natural feel - great for UI interactions */
97
+ easeOutCubic: (x: number): number => 1 - pow(1 - x, 3),
98
+
99
+ /** Smooth start and end - great for transitions */
100
+ easeInOutCubic: (x: number): number =>
101
+ x < 0.5 ? 4 * x * x * x : 1 - pow(-2 * x + 2, 3) / 2,
102
+
103
+ // ─────────────────────────────────────────────────────────────────────────────
104
+ // Quartic (power of 4)
105
+ // ─────────────────────────────────────────────────────────────────────────────
106
+
107
+ /** Slower start than cubic */
108
+ easeInQuart: (x: number): number => x * x * x * x,
109
+
110
+ /** Slower end than cubic */
111
+ easeOutQuart: (x: number): number => 1 - pow(1 - x, 4),
112
+
113
+ /** More pronounced ease than cubic */
114
+ easeInOutQuart: (x: number): number =>
115
+ x < 0.5 ? 8 * x * x * x * x : 1 - pow(-2 * x + 2, 4) / 2,
116
+
117
+ // ─────────────────────────────────────────────────────────────────────────────
118
+ // Quintic (power of 5)
119
+ // ─────────────────────────────────────────────────────────────────────────────
120
+
121
+ /** Very slow start */
122
+ easeInQuint: (x: number): number => x * x * x * x * x,
123
+
124
+ /** Very slow end */
125
+ easeOutQuint: (x: number): number => 1 - pow(1 - x, 5),
126
+
127
+ /** Very pronounced ease */
128
+ easeInOutQuint: (x: number): number =>
129
+ x < 0.5 ? 16 * x * x * x * x * x : 1 - pow(-2 * x + 2, 5) / 2,
130
+
131
+ // ─────────────────────────────────────────────────────────────────────────────
132
+ // Sinusoidal - Gentle, wave-like
133
+ // ─────────────────────────────────────────────────────────────────────────────
134
+
135
+ /** Gentle slow start */
136
+ easeInSine: (x: number): number => 1 - cos((x * PI) / 2),
137
+
138
+ /** Gentle slow end */
139
+ easeOutSine: (x: number): number => sin((x * PI) / 2),
140
+
141
+ /** Gentle ease both ends */
142
+ easeInOutSine: (x: number): number => -(cos(PI * x) - 1) / 2,
143
+
144
+ // ─────────────────────────────────────────────────────────────────────────────
145
+ // Exponential - Dramatic, snappy
146
+ // ─────────────────────────────────────────────────────────────────────────────
147
+
148
+ /** Dramatic slow start - almost stationary then fast */
149
+ easeInExpo: (x: number): number => (x === 0 ? 0 : pow(2, 10 * x - 10)),
150
+
151
+ /** Dramatic slow end - fast then almost stops */
152
+ easeOutExpo: (x: number): number => (x === 1 ? 1 : 1 - pow(2, -10 * x)),
153
+
154
+ /** Dramatic both ends */
155
+ easeInOutExpo: (x: number): number => {
156
+ if (x === 0) return 0
157
+ if (x === 1) return 1
158
+ if (x < 0.5) return pow(2, 20 * x - 10) / 2
159
+ return (2 - pow(2, -20 * x + 10)) / 2
160
+ },
161
+
162
+ // ─────────────────────────────────────────────────────────────────────────────
163
+ // Circular - Based on circle quarter
164
+ // ─────────────────────────────────────────────────────────────────────────────
165
+
166
+ /** Circular slow start */
167
+ easeInCirc: (x: number): number => 1 - sqrt(1 - pow(x, 2)),
168
+
169
+ /** Circular slow end */
170
+ easeOutCirc: (x: number): number => sqrt(1 - pow(x - 1, 2)),
171
+
172
+ /** Circular both ends */
173
+ easeInOutCirc: (x: number): number =>
174
+ x < 0.5
175
+ ? (1 - sqrt(1 - pow(2 * x, 2))) / 2
176
+ : (sqrt(1 - pow(-2 * x + 2, 2)) + 1) / 2,
177
+
178
+ // ─────────────────────────────────────────────────────────────────────────────
179
+ // Back - Overshoots then returns
180
+ // ─────────────────────────────────────────────────────────────────────────────
181
+
182
+ /** Pulls back before accelerating */
183
+ easeInBack: (x: number): number => c3 * x * x * x - c1 * x * x,
184
+
185
+ /** Overshoots target then settles */
186
+ easeOutBack: (x: number): number =>
187
+ 1 + c3 * pow(x - 1, 3) + c1 * pow(x - 1, 2),
188
+
189
+ /** Pulls back, overshoots, settles */
190
+ easeInOutBack: (x: number): number =>
191
+ x < 0.5
192
+ ? (pow(2 * x, 2) * ((c2 + 1) * 2 * x - c2)) / 2
193
+ : (pow(2 * x - 2, 2) * ((c2 + 1) * (x * 2 - 2) + c2) + 2) / 2,
194
+
195
+ // ─────────────────────────────────────────────────────────────────────────────
196
+ // Elastic - Spring-like oscillation
197
+ // ─────────────────────────────────────────────────────────────────────────────
198
+
199
+ /** Wobbles at start */
200
+ easeInElastic: (x: number): number => {
201
+ if (x === 0) return 0
202
+ if (x === 1) return 1
203
+ return -pow(2, 10 * x - 10) * sin((x * 10 - 10.75) * c4)
204
+ },
205
+
206
+ /** Wobbles at end - great for attention */
207
+ easeOutElastic: (x: number): number => {
208
+ if (x === 0) return 0
209
+ if (x === 1) return 1
210
+ return pow(2, -10 * x) * sin((x * 10 - 0.75) * c4) + 1
211
+ },
212
+
213
+ /** Wobbles both ends */
214
+ easeInOutElastic: (x: number): number => {
215
+ if (x === 0) return 0
216
+ if (x === 1) return 1
217
+ if (x < 0.5) return -(pow(2, 20 * x - 10) * sin((20 * x - 11.125) * c5)) / 2
218
+ return (pow(2, -20 * x + 10) * sin((20 * x - 11.125) * c5)) / 2 + 1
219
+ },
220
+
221
+ // ─────────────────────────────────────────────────────────────────────────────
222
+ // Bounce - Ball bouncing effect
223
+ // ─────────────────────────────────────────────────────────────────────────────
224
+
225
+ /** Bounces at start */
226
+ easeInBounce: (x: number): number => 1 - bounceOut(1 - x),
227
+
228
+ /** Bounces at end - like a ball settling */
229
+ easeOutBounce: bounceOut,
230
+
231
+ /** Bounces both ends */
232
+ easeInOutBounce: (x: number): number =>
233
+ x < 0.5 ? (1 - bounceOut(1 - 2 * x)) / 2 : (1 + bounceOut(2 * x - 1)) / 2,
234
+ } as const
235
+
236
+ /** All available easing function names */
237
+ export type EasingName = keyof typeof easings
238
+
239
+ /** An easing function signature */
240
+ export type EasingFunction = (progress: number) => number