@guinetik/gcanvas 1.0.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 (349) hide show
  1. package/.github/workflows/release.yaml +70 -0
  2. package/.jshintrc +4 -0
  3. package/.vscode/settings.json +22 -0
  4. package/CLAUDE.md +310 -0
  5. package/blackhole.jpg +0 -0
  6. package/demo.png +0 -0
  7. package/demos/CNAME +1 -0
  8. package/demos/animations.html +31 -0
  9. package/demos/basic.html +38 -0
  10. package/demos/baskara.html +31 -0
  11. package/demos/bezier.html +35 -0
  12. package/demos/beziersignature.html +29 -0
  13. package/demos/blackhole.html +28 -0
  14. package/demos/blob.html +35 -0
  15. package/demos/demos.css +289 -0
  16. package/demos/easing.html +28 -0
  17. package/demos/events.html +195 -0
  18. package/demos/fluent.html +647 -0
  19. package/demos/fractals.html +36 -0
  20. package/demos/genart.html +26 -0
  21. package/demos/gendream.html +26 -0
  22. package/demos/group.html +36 -0
  23. package/demos/home.html +587 -0
  24. package/demos/index.html +364 -0
  25. package/demos/isometric.html +34 -0
  26. package/demos/js/animations.js +452 -0
  27. package/demos/js/basic.js +204 -0
  28. package/demos/js/baskara.js +751 -0
  29. package/demos/js/bezier.js +692 -0
  30. package/demos/js/beziersignature.js +241 -0
  31. package/demos/js/blackhole/accretiondisk.obj.js +379 -0
  32. package/demos/js/blackhole/blackhole.obj.js +318 -0
  33. package/demos/js/blackhole/index.js +409 -0
  34. package/demos/js/blackhole/particle.js +56 -0
  35. package/demos/js/blackhole/starfield.obj.js +218 -0
  36. package/demos/js/blob.js +2263 -0
  37. package/demos/js/easing.js +477 -0
  38. package/demos/js/fluent.js +183 -0
  39. package/demos/js/fractals.js +931 -0
  40. package/demos/js/fractalworker.js +93 -0
  41. package/demos/js/genart.js +268 -0
  42. package/demos/js/gendream.js +209 -0
  43. package/demos/js/group.js +140 -0
  44. package/demos/js/info-toggle.js +25 -0
  45. package/demos/js/isometric.js +863 -0
  46. package/demos/js/kerr.js +1556 -0
  47. package/demos/js/lavalamp.js +590 -0
  48. package/demos/js/layout.js +354 -0
  49. package/demos/js/mondrian.js +285 -0
  50. package/demos/js/opacity.js +275 -0
  51. package/demos/js/painter.js +484 -0
  52. package/demos/js/particles-showcase.js +514 -0
  53. package/demos/js/particles.js +299 -0
  54. package/demos/js/patterns.js +397 -0
  55. package/demos/js/penrose/artifact.js +69 -0
  56. package/demos/js/penrose/blackhole.js +121 -0
  57. package/demos/js/penrose/constants.js +73 -0
  58. package/demos/js/penrose/game.js +943 -0
  59. package/demos/js/penrose/lore.js +278 -0
  60. package/demos/js/penrose/penrosescene.js +892 -0
  61. package/demos/js/penrose/ship.js +216 -0
  62. package/demos/js/penrose/sounds.js +211 -0
  63. package/demos/js/penrose/voidparticle.js +55 -0
  64. package/demos/js/penrose/voidscene.js +258 -0
  65. package/demos/js/penrose/voidship.js +144 -0
  66. package/demos/js/penrose/wormhole.js +46 -0
  67. package/demos/js/pipeline.js +555 -0
  68. package/demos/js/scene.js +304 -0
  69. package/demos/js/scenes.js +320 -0
  70. package/demos/js/schrodinger.js +410 -0
  71. package/demos/js/schwarzschild.js +1023 -0
  72. package/demos/js/shapes.js +628 -0
  73. package/demos/js/space/alien.js +171 -0
  74. package/demos/js/space/boom.js +98 -0
  75. package/demos/js/space/boss.js +353 -0
  76. package/demos/js/space/buff.js +73 -0
  77. package/demos/js/space/bullet.js +102 -0
  78. package/demos/js/space/constants.js +85 -0
  79. package/demos/js/space/game.js +1884 -0
  80. package/demos/js/space/hud.js +112 -0
  81. package/demos/js/space/laserbeam.js +179 -0
  82. package/demos/js/space/lightning.js +277 -0
  83. package/demos/js/space/minion.js +192 -0
  84. package/demos/js/space/missile.js +212 -0
  85. package/demos/js/space/player.js +430 -0
  86. package/demos/js/space/powerup.js +90 -0
  87. package/demos/js/space/starfield.js +58 -0
  88. package/demos/js/space/starpower.js +90 -0
  89. package/demos/js/spacetime.js +559 -0
  90. package/demos/js/svgtween.js +204 -0
  91. package/demos/js/tde/accretiondisk.js +418 -0
  92. package/demos/js/tde/blackhole.js +219 -0
  93. package/demos/js/tde/blackholescene.js +209 -0
  94. package/demos/js/tde/config.js +59 -0
  95. package/demos/js/tde/index.js +695 -0
  96. package/demos/js/tde/jets.js +290 -0
  97. package/demos/js/tde/lensedstarfield.js +147 -0
  98. package/demos/js/tde/tdestar.js +317 -0
  99. package/demos/js/tde/tidalstream.js +356 -0
  100. package/demos/js/tde_old/blackhole.obj.js +354 -0
  101. package/demos/js/tde_old/debris.obj.js +791 -0
  102. package/demos/js/tde_old/flare.obj.js +239 -0
  103. package/demos/js/tde_old/index.js +448 -0
  104. package/demos/js/tde_old/star.obj.js +812 -0
  105. package/demos/js/tiles.js +312 -0
  106. package/demos/js/tweendemo.js +79 -0
  107. package/demos/js/visibility.js +102 -0
  108. package/demos/kerr.html +28 -0
  109. package/demos/lavalamp.html +27 -0
  110. package/demos/layouts.html +37 -0
  111. package/demos/logo.svg +4 -0
  112. package/demos/loop.html +84 -0
  113. package/demos/mondrian.html +32 -0
  114. package/demos/og_image.png +0 -0
  115. package/demos/opacity.html +36 -0
  116. package/demos/painter.html +39 -0
  117. package/demos/particles-showcase.html +28 -0
  118. package/demos/particles.html +24 -0
  119. package/demos/patterns.html +33 -0
  120. package/demos/penrose-game.html +31 -0
  121. package/demos/pipeline.html +737 -0
  122. package/demos/scene.html +33 -0
  123. package/demos/scenes.html +96 -0
  124. package/demos/schrodinger.html +27 -0
  125. package/demos/schwarzschild.html +27 -0
  126. package/demos/shapes.html +16 -0
  127. package/demos/space.html +85 -0
  128. package/demos/spacetime.html +27 -0
  129. package/demos/svgtween.html +29 -0
  130. package/demos/tde.html +28 -0
  131. package/demos/tiles.html +28 -0
  132. package/demos/transforms.html +400 -0
  133. package/demos/tween.html +45 -0
  134. package/demos/visibility.html +33 -0
  135. package/disk_example.png +0 -0
  136. package/docs/README.md +222 -0
  137. package/docs/concepts/architecture-overview.md +204 -0
  138. package/docs/concepts/lifecycle.md +255 -0
  139. package/docs/concepts/rendering-pipeline.md +279 -0
  140. package/docs/concepts/tde-zorder.md +106 -0
  141. package/docs/concepts/two-layer-architecture.md +229 -0
  142. package/docs/getting-started/first-game.md +354 -0
  143. package/docs/getting-started/hello-world.md +269 -0
  144. package/docs/getting-started/installation.md +157 -0
  145. package/docs/modules/collision/README.md +453 -0
  146. package/docs/modules/fluent/README.md +1075 -0
  147. package/docs/modules/game/README.md +303 -0
  148. package/docs/modules/isometric-camera.md +210 -0
  149. package/docs/modules/isometric.md +275 -0
  150. package/docs/modules/painter/README.md +328 -0
  151. package/docs/modules/particle/README.md +559 -0
  152. package/docs/modules/shapes/README.md +221 -0
  153. package/docs/modules/shapes/base/euclidian.md +123 -0
  154. package/docs/modules/shapes/base/geometry2d.md +204 -0
  155. package/docs/modules/shapes/base/renderable.md +215 -0
  156. package/docs/modules/shapes/base/shape.md +262 -0
  157. package/docs/modules/shapes/base/transformable.md +243 -0
  158. package/docs/modules/shapes/hierarchy.md +218 -0
  159. package/docs/modules/state/README.md +577 -0
  160. package/docs/modules/util/README.md +99 -0
  161. package/docs/modules/util/camera3d.md +412 -0
  162. package/docs/modules/util/scene3d.md +395 -0
  163. package/index.html +17 -0
  164. package/jsdoc.json +50 -0
  165. package/package.json +55 -0
  166. package/readme.md +599 -0
  167. package/scripts/build-demo.js +69 -0
  168. package/scripts/bundle4llm.js +276 -0
  169. package/scripts/clearconsole.js +48 -0
  170. package/src/collision/collision-system.js +332 -0
  171. package/src/collision/collision.js +303 -0
  172. package/src/collision/index.js +10 -0
  173. package/src/fluent/fluent-game.js +430 -0
  174. package/src/fluent/fluent-go.js +1060 -0
  175. package/src/fluent/fluent-layer.js +152 -0
  176. package/src/fluent/fluent-scene.js +291 -0
  177. package/src/fluent/index.js +98 -0
  178. package/src/fluent/sketch.js +380 -0
  179. package/src/game/game.js +467 -0
  180. package/src/game/index.js +49 -0
  181. package/src/game/objects/go.js +220 -0
  182. package/src/game/objects/imagego.js +30 -0
  183. package/src/game/objects/index.js +54 -0
  184. package/src/game/objects/isometric-scene.js +260 -0
  185. package/src/game/objects/layoutscene.js +549 -0
  186. package/src/game/objects/scene.js +175 -0
  187. package/src/game/objects/scene3d.js +118 -0
  188. package/src/game/objects/text.js +221 -0
  189. package/src/game/objects/wrapper.js +232 -0
  190. package/src/game/pipeline.js +243 -0
  191. package/src/game/ui/button.js +396 -0
  192. package/src/game/ui/cursor.js +93 -0
  193. package/src/game/ui/fps.js +91 -0
  194. package/src/game/ui/index.js +5 -0
  195. package/src/game/ui/togglebutton.js +93 -0
  196. package/src/game/ui/tooltip.js +249 -0
  197. package/src/index.js +25 -0
  198. package/src/io/events.js +20 -0
  199. package/src/io/index.js +86 -0
  200. package/src/io/input.js +70 -0
  201. package/src/io/keys.js +152 -0
  202. package/src/io/mouse.js +61 -0
  203. package/src/io/touch.js +39 -0
  204. package/src/logger/debugtab.js +138 -0
  205. package/src/logger/index.js +3 -0
  206. package/src/logger/loggable.js +47 -0
  207. package/src/logger/logger.js +113 -0
  208. package/src/math/complex.js +37 -0
  209. package/src/math/constants.js +1 -0
  210. package/src/math/fractal.js +1271 -0
  211. package/src/math/gr.js +201 -0
  212. package/src/math/heat.js +202 -0
  213. package/src/math/index.js +12 -0
  214. package/src/math/noise.js +433 -0
  215. package/src/math/orbital.js +191 -0
  216. package/src/math/patterns.js +1339 -0
  217. package/src/math/penrose.js +259 -0
  218. package/src/math/quantum.js +115 -0
  219. package/src/math/random.js +195 -0
  220. package/src/math/tensor.js +1009 -0
  221. package/src/mixins/anchor.js +131 -0
  222. package/src/mixins/draggable.js +72 -0
  223. package/src/mixins/index.js +2 -0
  224. package/src/motion/bezier.js +132 -0
  225. package/src/motion/bounce.js +58 -0
  226. package/src/motion/easing.js +349 -0
  227. package/src/motion/float.js +130 -0
  228. package/src/motion/follow.js +125 -0
  229. package/src/motion/hop.js +52 -0
  230. package/src/motion/index.js +82 -0
  231. package/src/motion/motion.js +1124 -0
  232. package/src/motion/orbit.js +49 -0
  233. package/src/motion/oscillate.js +39 -0
  234. package/src/motion/parabolic.js +141 -0
  235. package/src/motion/patrol.js +147 -0
  236. package/src/motion/pendulum.js +48 -0
  237. package/src/motion/pulse.js +88 -0
  238. package/src/motion/shake.js +83 -0
  239. package/src/motion/spiral.js +144 -0
  240. package/src/motion/spring.js +150 -0
  241. package/src/motion/swing.js +47 -0
  242. package/src/motion/tween.js +92 -0
  243. package/src/motion/tweenetik.js +139 -0
  244. package/src/motion/waypoint.js +210 -0
  245. package/src/painter/index.js +8 -0
  246. package/src/painter/painter.colors.js +331 -0
  247. package/src/painter/painter.effects.js +230 -0
  248. package/src/painter/painter.img.js +229 -0
  249. package/src/painter/painter.js +295 -0
  250. package/src/painter/painter.lines.js +189 -0
  251. package/src/painter/painter.opacity.js +41 -0
  252. package/src/painter/painter.shapes.js +277 -0
  253. package/src/painter/painter.text.js +273 -0
  254. package/src/particle/emitter.js +124 -0
  255. package/src/particle/index.js +11 -0
  256. package/src/particle/particle-system.js +322 -0
  257. package/src/particle/particle.js +71 -0
  258. package/src/particle/updaters.js +170 -0
  259. package/src/shapes/arc.js +43 -0
  260. package/src/shapes/arrow.js +33 -0
  261. package/src/shapes/bezier.js +42 -0
  262. package/src/shapes/circle.js +62 -0
  263. package/src/shapes/clouds.js +56 -0
  264. package/src/shapes/cone.js +219 -0
  265. package/src/shapes/cross.js +70 -0
  266. package/src/shapes/cube.js +244 -0
  267. package/src/shapes/cylinder.js +254 -0
  268. package/src/shapes/diamond.js +48 -0
  269. package/src/shapes/euclidian.js +111 -0
  270. package/src/shapes/figure.js +115 -0
  271. package/src/shapes/geometry.js +220 -0
  272. package/src/shapes/group.js +375 -0
  273. package/src/shapes/heart.js +42 -0
  274. package/src/shapes/hexagon.js +26 -0
  275. package/src/shapes/image.js +192 -0
  276. package/src/shapes/index.js +111 -0
  277. package/src/shapes/line.js +29 -0
  278. package/src/shapes/pattern.js +90 -0
  279. package/src/shapes/pin.js +44 -0
  280. package/src/shapes/poly.js +31 -0
  281. package/src/shapes/prism.js +226 -0
  282. package/src/shapes/rect.js +35 -0
  283. package/src/shapes/renderable.js +333 -0
  284. package/src/shapes/ring.js +26 -0
  285. package/src/shapes/roundrect.js +95 -0
  286. package/src/shapes/shape.js +117 -0
  287. package/src/shapes/slice.js +26 -0
  288. package/src/shapes/sphere.js +314 -0
  289. package/src/shapes/sphere3d.js +537 -0
  290. package/src/shapes/square.js +15 -0
  291. package/src/shapes/star.js +99 -0
  292. package/src/shapes/svg.js +408 -0
  293. package/src/shapes/text.js +553 -0
  294. package/src/shapes/traceable.js +83 -0
  295. package/src/shapes/transform.js +357 -0
  296. package/src/shapes/transformable.js +172 -0
  297. package/src/shapes/triangle.js +26 -0
  298. package/src/sound/index.js +17 -0
  299. package/src/sound/sound.js +473 -0
  300. package/src/sound/synth.analyzer.js +149 -0
  301. package/src/sound/synth.effects.js +207 -0
  302. package/src/sound/synth.envelope.js +59 -0
  303. package/src/sound/synth.js +229 -0
  304. package/src/sound/synth.musical.js +160 -0
  305. package/src/sound/synth.noise.js +85 -0
  306. package/src/sound/synth.oscillators.js +293 -0
  307. package/src/state/index.js +10 -0
  308. package/src/state/state-machine.js +371 -0
  309. package/src/util/camera3d.js +438 -0
  310. package/src/util/index.js +6 -0
  311. package/src/util/isometric-camera.js +235 -0
  312. package/src/util/layout.js +317 -0
  313. package/src/util/position.js +147 -0
  314. package/src/util/tasks.js +47 -0
  315. package/src/util/zindex.js +287 -0
  316. package/src/webgl/index.js +9 -0
  317. package/src/webgl/shaders/sphere-shaders.js +994 -0
  318. package/src/webgl/webgl-renderer.js +388 -0
  319. package/tde.png +0 -0
  320. package/test/math/orbital.test.js +61 -0
  321. package/test/math/tensor.test.js +114 -0
  322. package/test/particle/emitter.test.js +204 -0
  323. package/test/particle/particle-system.test.js +310 -0
  324. package/test/particle/particle.test.js +116 -0
  325. package/test/particle/updaters.test.js +386 -0
  326. package/test/setup.js +120 -0
  327. package/test/shapes/euclidian.test.js +44 -0
  328. package/test/shapes/geometry.test.js +86 -0
  329. package/test/shapes/group.test.js +86 -0
  330. package/test/shapes/rectangle.test.js +64 -0
  331. package/test/shapes/transform.test.js +379 -0
  332. package/test/util/camera3d.test.js +428 -0
  333. package/test/util/scene3d.test.js +352 -0
  334. package/types/collision.d.ts +249 -0
  335. package/types/common.d.ts +155 -0
  336. package/types/game.d.ts +497 -0
  337. package/types/index.d.ts +309 -0
  338. package/types/io.d.ts +188 -0
  339. package/types/logger.d.ts +127 -0
  340. package/types/math.d.ts +268 -0
  341. package/types/mixins.d.ts +92 -0
  342. package/types/motion.d.ts +678 -0
  343. package/types/painter.d.ts +378 -0
  344. package/types/shapes.d.ts +864 -0
  345. package/types/sound.d.ts +672 -0
  346. package/types/state.d.ts +251 -0
  347. package/types/util.d.ts +253 -0
  348. package/vite.config.js +50 -0
  349. package/vitest.config.js +13 -0
package/readme.md ADDED
@@ -0,0 +1,599 @@
1
+ # GCanvas ๐ŸŽจ
2
+
3
+ ![demo screenshot](./demo.png)
4
+
5
+ A minimalist 2D canvas rendering library built for learning, expression, and creative coding.
6
+
7
+ Inspired by the simplicity of p5.js and the composability of game engines โ€” **GCanvas** gives you structured primitives, a game loop, interactivity, and a growing set of intuitive, declarative shapes.
8
+
9
+ ---
10
+
11
+ # What is this?
12
+
13
+ GCanvas is a modular 2D rendering and game framework built on top of the HTML5 Canvas API.
14
+
15
+ At its core, it's split into two powerful layers:
16
+
17
+ - ๐ŸŽจ **Shape Layer** โ€” for clean, declarative drawing via transformable primitives like `Circle`, `Rectangle`, and `Group`.
18
+ - ๐ŸŽฎ **Game Layer** โ€” for interactive, updatable `GameObject`s managed through a pipeline and real-time loop.
19
+
20
+ You can use GCanvas purely for visuals, or build full simulations, games, or creative tools on top.
21
+
22
+ ---
23
+
24
+ ## ๐ŸŒŸ Features
25
+
26
+ - ๐Ÿ’Ž **Primitives** like `Circle`, `Line`, `Rectangle`, `Star`, `Polygon`, and more
27
+ - โšฝ **2.5D Shapes** like `Cube`, `Cylinder`, `Prism`, `Sphere`, `Cone`
28
+ - ๐Ÿ“ฆ **Groups** for transformable shape collections
29
+ - ๐Ÿ“ˆ **Transforms** โ€” rotation, scale, constraints, group nesting
30
+ - ๐Ÿ–Œ๏ธ **Painter API** for imperative canvas control
31
+ - ๐Ÿ‘พ **GameObjects** for updatable, interactive entities
32
+ - ๐Ÿ“ฑ **UI Components** like `Button`, `ToggleButton`, `Cursor`, and layout managers
33
+ - ๐ŸŽž๏ธ **Motion API** for game-time-driven animations
34
+ - ๐Ÿ“… **Tweenetik** for UI-style, self-running tweens
35
+ - ๐Ÿ˜ **Event system** โ€” hover, click, mouse, and touch
36
+ - ๐Ÿš€ **Works with Vite, Rollup, or standalone**
37
+
38
+ ---
39
+
40
+ ## ๐Ÿ“Š Demo
41
+
42
+ Open [`https://gcanvas.guinetik.com`](https://gcanvas.guinetik.com) or run:
43
+
44
+ ```bash
45
+ npm run dev
46
+ ```
47
+
48
+ ## ๐Ÿ”ข API Docs
49
+
50
+ Each shape and utility is JSDoc-annotated.
51
+ I'm organizing a docs folder on Github Pages.
52
+ For now, you can do `npm run docs` to generate the docs site.
53
+
54
+ ### TypeScript Support
55
+
56
+ GCanvas includes TypeScript definitions (`gcanvas.d.ts`) for full IDE intellisense and type checking. The types are automatically included when you install the package.
57
+
58
+ ## ๐Ÿง‘โ€๐Ÿ’ป Installation
59
+
60
+ ๏ธโš ๏ธComing soon to NPM.
61
+
62
+ For now, clone this repo:
63
+
64
+ ```bash
65
+ git clone https://github.com/guinetik/gcanvas.git
66
+ cd gcanvas
67
+ npm install
68
+ ```
69
+
70
+ To run our tech demos:
71
+
72
+ ```bash
73
+ npm run dev
74
+ ```
75
+
76
+ To build the library:
77
+
78
+ ```bash
79
+ npm run build
80
+ ```
81
+
82
+ To generate a readable single-file debug build:
83
+
84
+ ```bash
85
+ npm run build:debug
86
+ ```
87
+
88
+ ---
89
+
90
+ ## ๐Ÿš€ Quick Start
91
+
92
+ ### Using via ESM:
93
+
94
+ ```js
95
+ import { Circle } from './dist/gcanvas.es.min.js';
96
+
97
+ const circle = new Circle(100, 100, 50, { fillColor: 'red' });
98
+ circle.draw(); // uses static Painter.ctx internally
99
+ ```
100
+
101
+ ### Using via inline script:
102
+
103
+ ```html
104
+ <script src="./dist/gcanvas.umd.min.js"></script>
105
+ <script>
106
+ const circle = new GCanvas.Circle(200, 200, 40, { fillColor: 'blue' });
107
+ circle.draw();
108
+ </script>
109
+ ```
110
+
111
+ ---
112
+
113
+ ## ๐Ÿง  Core Architecture
114
+
115
+ Our engine follows a layered architecture that builds from simple drawing primitives up to interactive game objects and scenes.
116
+
117
+ ### ๐Ÿ–Œ๏ธ Painter Layer
118
+
119
+ At the foundation is `Painter` - a wrapper around the canvas context that centralizes all drawing operations. Instead of working with the Canvas API directly, we use `Painter` as an intermediate layer:
120
+
121
+ ```js
122
+ // Painter is initialized with a canvas context
123
+ Painter.init(ctx);
124
+
125
+ // Now drawing operations use Painter's methods
126
+ Painter.shapes.circle(100, 100, 50, { fillColor: "red" });
127
+ Painter.shapes.rect(200, 200, 80, 40, { strokeColor: "blue" });
128
+ ```
129
+
130
+ ### ๐Ÿ”„ Transformable Properties
131
+
132
+ Next we have `Transformable`, which provides standard properties for positioning and displaying objects:
133
+
134
+ - Position (`x`, `y`)
135
+ - Size (`width`, `height`)
136
+ - Visual properties (opacity, visibility)
137
+ - Transformations (rotation, scaleX, scaleY)
138
+
139
+ Every visual element in the engine inherits from Transformable.
140
+
141
+ ### ๐Ÿ’Ž Shape Layer
142
+
143
+ The Shape Layer is built on a hierarchy of classes that provide increasingly sophisticated rendering capabilities:
144
+
145
+ 1. **Euclidian** - The base class that defines spatial properties (x, y, width, height)
146
+ 2. **Geometry2d** - Adds bounding box calculations and constraints
147
+ 3. **Transformable** - Introduces rotation and scaling capabilities
148
+ 4. **Renderable** - Adds visual properties like opacity and visibility
149
+ 5. **Shape** - The final layer that adds styling (fill, stroke, line width)
150
+
151
+ Each layer uses underscore-prefixed private fields (`_property`) for encapsulation, following a consistent pattern throughout the codebase:
152
+
153
+ ```js
154
+ class Shape extends Transformable {
155
+ constructor(options = {}) {
156
+ super(options);
157
+ this._fillColor = options.fillColor ?? null;
158
+ this._strokeColor = options.strokeColor ?? null;
159
+ this._lineWidth = options.lineWidth ?? 1;
160
+ }
161
+
162
+ get fillColor() { return this._fillColor; }
163
+ set fillColor(v) { this._fillColor = v; }
164
+ }
165
+ ```
166
+
167
+ The engine provides standard shapes like `Circle`, `Rectangle`, `Star`, etc., all built on this foundation.
168
+
169
+ ### ๐Ÿ“ฆ Group
170
+
171
+ `Group` is a special shape that contains other shapes. It applies its transforms to all children, allowing composite visuals to move and transform as a unit:
172
+
173
+ ```js
174
+ const group = new Group(400, 300);
175
+ group.add(new Circle(0, 0, 40, { fillColor: "red" }));
176
+ group.add(new Rectangle(0, 60, 80, 30, { fillColor: "blue" }));
177
+
178
+ // All shapes draw relative to the group position
179
+ group.draw();
180
+ ```
181
+
182
+ ### ๐Ÿชœ Pipeline
183
+
184
+ The `Pipeline` manages collections of objects that need to be updated and rendered each frame. It handles:
185
+
186
+ - The update/render sequence
187
+ - Addition/removal of objects
188
+ - Input event dispatch to the proper objects
189
+
190
+ ```js
191
+ // Create and add objects to the pipeline
192
+ pipeline.add(player);
193
+ pipeline.add(enemy);
194
+
195
+ // Pipeline handles updates in sequence
196
+ pipeline.update(dt);
197
+ pipeline.render();
198
+ ```
199
+
200
+ ### ๐Ÿ•น๏ธ Game
201
+
202
+ The `Game` class brings everything together with:
203
+
204
+ - Canvas management
205
+ - Animation frame timing
206
+ - Input system initialization
207
+ - Pipeline management
208
+ - Motion system integration
209
+
210
+ It's the core engine that drives everything:
211
+
212
+ ```js
213
+ const game = new Game(canvasElement);
214
+ game.init(); // Set up your game
215
+ game.start(); // Start the game loop
216
+ ```
217
+
218
+ ### โ˜„๏ธ GameObject
219
+
220
+ `GameObject` extends `Transformable` and adds lifecycle methods:
221
+
222
+ - `update(dt)` โ€” Run game logic each frame
223
+ - `render()` โ€” Optional custom rendering
224
+ - Event handling through `enableInteractivity(shape)`
225
+
226
+ This is the base class for all interactive entities:
227
+
228
+ ```js
229
+ class Player extends GameObject {
230
+ constructor(game) {
231
+ super(game);
232
+ this.shape = new Circle(100, 100, 40, { fillColor: "blue" });
233
+ this.enableInteractivity(this.shape);
234
+ }
235
+
236
+ update(dt) {
237
+ // Move based on input
238
+ if (this.game.input.isKeyDown('ArrowRight')) {
239
+ this.x += 200 * dt;
240
+ }
241
+ }
242
+
243
+ render() {
244
+ this.shape.draw();
245
+ }
246
+
247
+ onPointerDown(e) {
248
+ console.log('Player clicked!');
249
+ }
250
+ }
251
+ ```
252
+
253
+ ### ๐Ÿ–ผ๏ธ Scene
254
+
255
+ `Scene` is a special GameObject that manages child GameObjects:
256
+
257
+ - Organizes objects into logical groups
258
+ - Handles nested input events
259
+ - Manages coordinate systems for children
260
+
261
+ ```js
262
+ // Create a level scene
263
+ const level = new Scene(game);
264
+ level.add(new Player(game));
265
+ level.add(new Enemy(game));
266
+
267
+ // Add scene to the game
268
+ game.pipeline.add(level);
269
+ ```
270
+
271
+ ### ๐Ÿ“ฑ UI System
272
+
273
+ Built on top of `GameObject` and `Scene`, the UI system provides:
274
+
275
+ - `Button` โ€” Clickable elements with visual states
276
+ - `ToggleButton` โ€” On/off toggleable buttons
277
+ - `Cursor` โ€” Custom cursor shapes
278
+ - Layout containers for automatic positioning:
279
+ - `HorizontalLayout`
280
+ - `VerticalLayout`
281
+ - `TileLayout`
282
+
283
+ ```js
284
+ const ui = new Scene(game);
285
+ const menu = new VerticalLayout(game, {
286
+ spacing: 20,
287
+ align: "center"
288
+ });
289
+
290
+ menu.add(new Button(game, "Play"));
291
+ menu.add(new Button(game, "Options"));
292
+ menu.add(new Button(game, "Quit"));
293
+
294
+ ui.add(menu);
295
+ game.pipeline.add(ui);
296
+ ```
297
+
298
+ All UI elements integrate with the animation systems for smooth transitions and effects.
299
+
300
+ ## Building with Layers
301
+
302
+ This architecture lets you work at the appropriate level of abstraction:
303
+
304
+ 1. **Simple visuals**: Use Shape and Group directly
305
+ 2. **Interactive elements**: Create GameObjects
306
+ 3. **Organized worlds**: Use Scenes to structure content
307
+ 4. **Complete games**: Subclass Game with your own logic
308
+ 5. **Rich interfaces**: Add UI elements for player interaction
309
+
310
+ Each layer builds on the ones below, creating a flexible and powerful framework for 2D canvas games.
311
+
312
+ ---
313
+
314
+ ## ๐ŸŽž๏ธ Motion System
315
+
316
+ The framework provides standard easing functions along with two complementary animation systems:
317
+
318
+ ### 1. `Motion` - Stateless animation primitives
319
+
320
+ The `Motion` system provides mathematical animation functions that calculate positions based on time input. These functions don't modify objects directly - instead they return values that you apply in your game logic.
321
+
322
+ ```js
323
+ class Asteroid extends GameObject {
324
+ constructor(game) {
325
+ super(game);
326
+ this.shape = new Circle(0, 0, 40, { fillColor: "#555" });
327
+ this.time = 0;
328
+ }
329
+
330
+ update(dt) {
331
+ this.time += dt;
332
+
333
+ // Get position from a spiral pattern
334
+ const result = Motion.spiral(
335
+ 400, 300, // center point
336
+ 50, 200, // start and end radius
337
+ 0, 3, // start angle, revolutions
338
+ this.time, 5, // current time, duration
339
+ true, // loop
340
+ Easing.easeInOutSine
341
+ );
342
+
343
+ // Apply the result to our shape
344
+ this.shape.x = result.x;
345
+ this.shape.y = result.y;
346
+ }
347
+
348
+ render() {
349
+ this.shape.draw();
350
+ }
351
+ }
352
+ ```
353
+
354
+ Motion functions:
355
+ - `bezier` - Follows along a cubic bezier curve.
356
+ - `bounce` - Objects that drop and bounce with decreasing height
357
+ - `float` - Natural floating motion with randomness
358
+ - `follow` - Follow paths of waypoints with turning
359
+ - `orbit` - Circular or elliptical orbits
360
+ - `oscillate` - Values that cycle between min and max
361
+ - `parabolic` - Arcing projectile motion
362
+ - `patrol` - Entities that wander within bounds
363
+ - `pendulum` - Swinging motion around a pivot (cosine)
364
+ - `pulse` - Values that pulse up and down
365
+ - `shake` - Camera or object shake effects
366
+ - `spiral` - Spiral paths with variable radius
367
+ - `spring` - Physics-based spring motion
368
+ - `swing` - Rotational swinging (sine)
369
+ - `waypoint` - Character movement between points
370
+
371
+ Each function returns standardized results with position, rotation, and animation metadata like progress and completion state.
372
+
373
+ Key advantages:
374
+ - Deterministic (same input time = same output)
375
+ - No state to track between frames
376
+ - Compatible with any rendering system
377
+ - Composable through grouping and sequencing
378
+
379
+ ### 2. `Tweenetik` - Property animation
380
+
381
+ The `Tweenetik` system animates object properties directly over time using easing functions:
382
+
383
+ ```js
384
+ // Animate a button when pressed
385
+ onPointerDown() {
386
+ Tweenetik.to(this.shape,
387
+ { scaleX: 1.2, scaleY: 1.2 },
388
+ 0.2,
389
+ Easing.easeOutBack,
390
+ {
391
+ onComplete: () => {
392
+ Tweenetik.to(this.shape,
393
+ { scaleX: 1.0, scaleY: 1.0 },
394
+ 0.3,
395
+ Easing.easeInOutQuad
396
+ );
397
+ }
398
+ }
399
+ );
400
+ }
401
+ ```
402
+
403
+ Tweenetik is ideal for:
404
+ - UI animations and transitions
405
+ - Simple property tweens
406
+ - Sequential animations with callbacks
407
+
408
+ All animations integrate with the game's update/render cycle. Tweenetik is automatically updated by the Pipeline, while Motion functions are called directly in your GameObject's update method.
409
+
410
+ ### 3. `Easing` - Timing functions
411
+
412
+ Both systems use the same easing functions for consistent animation curves:
413
+
414
+ ```js
415
+ Easing.easeInQuad // accelerating
416
+ Easing.easeOutBounce // bouncy ending
417
+ Easing.easeInOutCubic // smooth acceleration and deceleration
418
+ // ...and many more
419
+ ```
420
+
421
+ These functions transform linear time (0-1) into curved progressions for natural motion.
422
+
423
+ ### ๐Ÿงฉ Mixins
424
+
425
+ Mixins are behavior modules that add optional functionality to any `GameObject`. They're lightweight and composable: apply only what you need.
426
+
427
+ Current mixins include:
428
+
429
+ - `applyAnchor(go, options)` โ€” anchors the GameObject to a screen position like `"top-left"`, `"bottom-right"`, etc. Automatically repositions on resize.
430
+ - `applyDraggable(go, shape, options)` โ€” enables drag-and-drop behavior with pointer support and optional friction.
431
+
432
+ Example:
433
+
434
+ ```js
435
+ applyAnchor(myUIElement, { anchor: "top-right", padding: 20 });
436
+ applyDraggable(myCard, myCard.shape);
437
+ ```
438
+
439
+ You can combine multiple mixins to build rich UI/GameObject interactions without subclassing.
440
+
441
+ Mixins can also be passed as options:
442
+ ```js
443
+ // Add FPS counter
444
+ this.scene.add(
445
+ new FPSCounter(game, {
446
+ anchor: "bottom-right",
447
+ })
448
+ );
449
+ ```
450
+
451
+ ...
452
+
453
+ ## ๐ŸŽ“ Example
454
+
455
+ ### Hello World
456
+ ```html
457
+ <html lang="en">
458
+ <head>
459
+ <meta charset="UTF-8" />
460
+ <title>Basic Game Template</title>
461
+ </head>
462
+ <body>
463
+ <canvas id="game"></canvas>
464
+ <script type="module">
465
+ import { Game, Scene, GameObject, FPSCounter, Rectangle, TextShape, Group } from "gcanvas";
466
+ class HelloWorld extends Game {
467
+ constructor(canvas) {
468
+ super(canvas);
469
+ this.enableFluidSize();
470
+ this.backgroundColor = "black";
471
+ }
472
+ init() {
473
+ // Set up scenes
474
+ this.scene = new Scene(this);
475
+ this.ui = new Scene(this);
476
+ this.pipeline.add(this.scene); // game layer
477
+ this.pipeline.add(this.ui); // UI layer
478
+ // Hello World box in the game scene
479
+ this.scene.add(new HelloWorldBox(this));
480
+ // FPS counter in the UI scene
481
+ this.ui.add(new FPSCounter(this, { anchor: "bottom-right" }));
482
+ }
483
+ }
484
+
485
+ //A Simple game object with a Shape and a Text box
486
+ class HelloWorldBox extends GameObject {
487
+ constructor(game) {
488
+ super(game);
489
+
490
+ const box = new Rectangle(0, 0, 200, 80, {
491
+ fillColor: "#111",
492
+ strokeColor: "#0f0",
493
+ lineWidth: 2,
494
+ });
495
+
496
+ const label = new TextShape(0, 0, "Hello World!", {
497
+ font: "18px monospace",
498
+ color: "#0f0",
499
+ align: "center",
500
+ baseline: "middle"
501
+ });
502
+
503
+ this.group = new Group(game.width / 2, game.height / 2);
504
+ this.group.add(box);
505
+ this.group.add(label);
506
+ }
507
+ /**
508
+ * Render the Group
509
+ */
510
+ render() {
511
+ this.group.draw();
512
+ }
513
+ }
514
+
515
+ window.addEventListener("load", () => {
516
+ const canvas = document.getElementById("game");
517
+ const game = new HelloWorld(canvas);
518
+ game.init();
519
+ game.start();
520
+ });
521
+ </script>
522
+ </body>
523
+
524
+ </html>
525
+ ```
526
+
527
+ ### ๐ŸŒŸ GameObject with Shape
528
+
529
+ ```js
530
+ class Bob extends GameObject {
531
+ constructor(game) {
532
+ super(game);
533
+ this.shape = new Circle(100, 100, 40, { fillColor: "tomato" });
534
+ this.enableInteractivity(this.shape);
535
+ }
536
+
537
+ update(dt) {
538
+ this.shape.x += Math.sin(Date.now() * 0.001) * dt * 60;
539
+ }
540
+
541
+ render() {
542
+ this.shape.draw();
543
+ }
544
+ }
545
+ ```
546
+
547
+ If you want to quickly wrap a `Shape` in a `GameObject` without writing a full class, you can use `ShapeGOFactory.create(...)`:
548
+
549
+ ```js
550
+ const shape = new Rectangle(0, 0, 100, 50, { fillColor: "lime" });
551
+ const go = ShapeGOFactory.create(game, shape);
552
+ game.pipeline.add(go);
553
+ ```
554
+
555
+ This creates a `GameObjectShapeWrapper` behind the scenes that keeps the transform in sync and draws the shape on each frame.
556
+
557
+ ### ๐Ÿ’ซ Spinning Shape on Hover
558
+
559
+ ```js
560
+ class SpinningShape extends GameObject {
561
+ constructor(game) {
562
+ super(game);
563
+ this.shape = new Circle(200, 200, 50, { fillColor: 'cyan' });
564
+ this.enableInteractivity(this.shape);
565
+ this.hovered = false;
566
+
567
+ this.on('mouseover', () => this.hovered = true);
568
+ this.on('mouseout', () => this.hovered = false);
569
+ }
570
+
571
+ update(dt) {
572
+ if (this.hovered) this.shape.rotation += 2 * dt;
573
+ }
574
+
575
+ render() {
576
+ this.shape.draw();
577
+ }
578
+ }
579
+ ```
580
+
581
+ ---
582
+
583
+ ## ๐Ÿ“‚ File Structure
584
+
585
+ ```bash
586
+ src/
587
+ โ”œโ”€โ”€ shapes/ # All shape definitions (Circle, Triangle, Cube, etc.)
588
+ โ”œโ”€โ”€ motion/ # Motion & Tweening systems
589
+ โ”œโ”€โ”€ io/ # Mouse, Touch, Input
590
+ โ”œโ”€โ”€ game/ # Game loop, GameObject, Pipeline
591
+ โ”œโ”€โ”€ painter.js # Canvas context abstraction
592
+ โ”œโ”€โ”€ index.js # Public API entry point
593
+ ```
594
+
595
+ ---
596
+ ```
597
+ "jsdoc": "^4.0.4"
598
+ "better-docs": "^2.7.3"
599
+ ```
@@ -0,0 +1,69 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { fileURLToPath } from "url";
4
+ import { execSync } from "child_process";
5
+
6
+ const __filename = fileURLToPath(import.meta.url);
7
+ const __dirname = path.dirname(__filename);
8
+
9
+ const ROOT = path.join(__dirname, "..");
10
+ const DEMOS = path.join(ROOT, "demos");
11
+ const DIST = path.join(ROOT, "dist");
12
+ const PUBLIC = path.join(ROOT, "public");
13
+ const ESM_FILE = path.join(DIST, "gcanvas.es.min.js");
14
+
15
+ function cleanPublic() {
16
+ if (fs.existsSync(PUBLIC)) fs.rmSync(PUBLIC, { recursive: true });
17
+ fs.mkdirSync(PUBLIC);
18
+ }
19
+
20
+ function copyRecursive(src, dest) {
21
+ fs.mkdirSync(dest, { recursive: true });
22
+ for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
23
+ const srcPath = path.join(src, entry.name);
24
+ const destPath = path.join(dest, entry.name);
25
+ if (entry.isDirectory()) copyRecursive(srcPath, destPath);
26
+ else fs.copyFileSync(srcPath, destPath);
27
+ }
28
+ }
29
+
30
+ function rewriteImports(dir) {
31
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
32
+ const fullPath = path.join(dir, entry.name);
33
+
34
+ if (entry.isDirectory()) {
35
+ rewriteImports(fullPath);
36
+ } else if (entry.isFile() && /\.(js|html)$/.test(entry.name)) {
37
+ let content = fs.readFileSync(fullPath, "utf-8");
38
+
39
+ // Replace all imports pointing to any form of src/
40
+ content = content.replace(
41
+ /from\s+["'](\.{1,2}\/)+src\/[^"']+["']/g,
42
+ `from "/gcanvas.es.min.js"`
43
+ );
44
+
45
+ fs.writeFileSync(fullPath, content);
46
+ }
47
+ }
48
+ }
49
+
50
+ function run() {
51
+ console.log("๐Ÿงน Cleaning public/");
52
+ cleanPublic();
53
+
54
+ console.log("๐Ÿ“ฆ Running build");
55
+ execSync("npm run build", { stdio: "inherit" });
56
+
57
+ console.log("๐Ÿ“ Copying demos โ†’ public/");
58
+ copyRecursive(DEMOS, PUBLIC);
59
+
60
+ console.log("๐Ÿ“ Copying gcanvas.es.min.js โ†’ public/");
61
+ fs.copyFileSync(ESM_FILE, path.join(PUBLIC, "gcanvas.es.min.js"));
62
+
63
+ console.log("โœ๏ธ Rewriting imports in public/...");
64
+ rewriteImports(PUBLIC);
65
+
66
+ console.log("โœ… build:demo complete");
67
+ }
68
+
69
+ run();