@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
@@ -0,0 +1,1075 @@
1
+ # Fluent Module
2
+
3
+ > Declarative, chainable API for rapid game development.
4
+
5
+ ## Overview
6
+
7
+ The Fluent module provides a builder-pattern API layer on top of GCanvas's object-oriented architecture. Instead of manually instantiating classes and wiring them together, you chain method calls to declaratively build your game structure.
8
+
9
+ ### Traditional API vs Fluent API
10
+
11
+ **Traditional approach:**
12
+ ```js
13
+ import { Game, Scene, GameObject, Circle } from 'gcanvas';
14
+
15
+ const canvas = document.getElementById('game');
16
+ const game = new Game(canvas);
17
+ game.init();
18
+ game.backgroundColor = 'black';
19
+
20
+ const scene = new Scene(game);
21
+ scene.name = 'game';
22
+ game.pipeline.add(scene);
23
+
24
+ const player = new GameObject({ x: 400, y: 300 });
25
+ player.game = game;
26
+ const circle = new Circle(30, { color: 'lime' });
27
+ player.setRenderable(circle);
28
+ scene.add(player);
29
+
30
+ game.start();
31
+ ```
32
+
33
+ **Fluent approach:**
34
+ ```js
35
+ import { gcanvas } from 'gcanvas';
36
+
37
+ gcanvas({ bg: 'black' })
38
+ .scene('game')
39
+ .go({ x: 400, y: 300, name: 'player' })
40
+ .circle({ radius: 30, fill: 'lime' })
41
+ .start();
42
+ ```
43
+
44
+ ### Two Entry Points
45
+
46
+ | Entry Point | Purpose | Best For |
47
+ |-------------|---------|----------|
48
+ | `gcanvas(options)` | Full game development API | Games, interactive apps, complex scenes |
49
+ | `sketch(w, h, bg)` | Minimal creative coding API | Quick prototypes, generative art, Genuary-style sketches |
50
+
51
+ ---
52
+
53
+ ## Quick Start
54
+
55
+ ```js
56
+ import { gcanvas } from 'gcanvas';
57
+
58
+ // Create a game with a pulsing circle
59
+ gcanvas({ bg: '#1a1a2e' })
60
+ .scene('game')
61
+ .go({ x: 400, y: 300 })
62
+ .circle({ radius: 40, fill: '#00ff88' })
63
+ .pulse({ min: 0.8, max: 1.2, duration: 1 })
64
+ .start();
65
+ ```
66
+
67
+ ---
68
+
69
+ ## Entry Points
70
+
71
+ ### gcanvas(options)
72
+
73
+ The main entry point for the full fluent API.
74
+
75
+ ```js
76
+ import { gcanvas } from 'gcanvas';
77
+
78
+ const game = gcanvas({
79
+ canvas: document.getElementById('game'), // Optional: use existing canvas
80
+ width: 800, // Canvas width (default: 800)
81
+ height: 600, // Canvas height (default: 600)
82
+ bg: 'black', // Background color
83
+ fluid: true, // Enable responsive sizing (default: true)
84
+ fps: 60, // Target FPS (default: 60)
85
+ container: document.body, // Container for auto-created canvas
86
+ pixelRatio: window.devicePixelRatio // Pixel ratio for HiDPI
87
+ });
88
+ ```
89
+
90
+ **Returns:** `FluentGame` instance
91
+
92
+ ### sketch(w, h, bg)
93
+
94
+ Ultra-simple mode for quick creative coding prototypes.
95
+
96
+ ```js
97
+ import { sketch } from 'gcanvas';
98
+
99
+ sketch(800, 600, '#1a1a1a')
100
+ .circle(400, 300, 50, 'lime')
101
+ .update((dt, ctx) => {
102
+ ctx.shapes[0].x += Math.sin(ctx.time) * 2;
103
+ })
104
+ .start();
105
+ ```
106
+
107
+ **Parameters:**
108
+ - `w` - Canvas width (default: 800)
109
+ - `h` - Canvas height (default: 600)
110
+ - `bg` - Background color (default: 'black')
111
+
112
+ **Returns:** `SketchAPI` object
113
+
114
+ ---
115
+
116
+ ## Builder Chain Architecture
117
+
118
+ The fluent API uses a chain of builder classes that wrap the underlying GCanvas classes:
119
+
120
+ ```
121
+ FluentGame ─────────── Wraps Game
122
+
123
+ └── FluentScene ──── Wraps Scene
124
+
125
+ └── FluentGO ─── Wraps GameObject
126
+
127
+ └── FluentGO (children)
128
+ ```
129
+
130
+ ### Navigation
131
+
132
+ Each builder provides methods to navigate the chain:
133
+
134
+ ```js
135
+ gcanvas({ bg: 'black' })
136
+ .scene('game') // → FluentScene
137
+ .go({ x: 100, y: 100 }) // → FluentGO
138
+ .circle({ radius: 20 })
139
+ .end() // ← Back to FluentScene
140
+ .go({ x: 200, y: 100 }) // → Another FluentGO
141
+ .rect({ width: 40, height: 40 })
142
+ .scene('ui') // → New FluentScene (auto-navigates up)
143
+ .go({ x: 20, y: 20 })
144
+ .text('Score: 0')
145
+ .start(); // Start the game
146
+ ```
147
+
148
+ ### Shared Context
149
+
150
+ All builders share access to:
151
+ - **refs** - Named object references (objects with `name` property)
152
+ - **state** - Shared state object for game data
153
+
154
+ ---
155
+
156
+ ## FluentGame API
157
+
158
+ The root builder class wrapping the `Game` instance.
159
+
160
+ ### Scene Management
161
+
162
+ #### .scene(name, options)
163
+
164
+ Create or switch to a scene.
165
+
166
+ ```js
167
+ // Basic scene creation
168
+ .scene('game')
169
+
170
+ // With options
171
+ .scene('game', {
172
+ zIndex: 0, // Render order
173
+ active: true, // Visibility
174
+ onEnter: (ctx) => {}, // Called when scene becomes visible
175
+ onExit: (ctx) => {} // Called when scene hides
176
+ })
177
+
178
+ // With custom Scene class
179
+ .scene('game', MyCustomScene)
180
+ .scene('game', MyCustomScene, { zIndex: 0 })
181
+ ```
182
+
183
+ **Signatures:**
184
+ - `scene(name)` - Create/switch to named scene
185
+ - `scene(name, options)` - Create with options
186
+ - `scene(name, CustomSceneClass)` - Custom class
187
+ - `scene(name, CustomSceneClass, options)` - Custom class with options
188
+ - `scene(CustomSceneClass)` - Custom class, name from class name
189
+ - `scene(CustomSceneClass, options)` - Custom class with options
190
+
191
+ #### .inScene(name)
192
+
193
+ Switch to an existing scene without creating it. Throws if scene doesn't exist.
194
+
195
+ ```js
196
+ .scene('game')
197
+ .go({ name: 'player' })
198
+ .circle({ radius: 20 })
199
+ .scene('ui')
200
+ .go({ name: 'score' })
201
+ .text('0')
202
+ // Later, switch back without recreating
203
+ .inScene('game')
204
+ .go({ name: 'enemy' })
205
+ .rect({ width: 30, height: 30 })
206
+ ```
207
+
208
+ #### .go(options)
209
+
210
+ Shortcut to create a GameObject in the current scene.
211
+
212
+ ```js
213
+ gcanvas({ bg: 'black' })
214
+ .go({ x: 400, y: 300 }) // Creates 'default' scene automatically
215
+ .circle({ radius: 30 })
216
+ .start();
217
+ ```
218
+
219
+ ### Scene Visibility
220
+
221
+ #### .showScene(name)
222
+
223
+ Show a scene and trigger its `onEnter` callback.
224
+
225
+ #### .hideScene(name)
226
+
227
+ Hide a scene and trigger its `onExit` callback.
228
+
229
+ #### .transition(from, to, options)
230
+
231
+ Transition between scenes.
232
+
233
+ ```js
234
+ .transition('menu', 'game', {
235
+ fade: 0.5, // Fade duration in seconds
236
+ onComplete: () => {} // Completion callback
237
+ })
238
+ ```
239
+
240
+ ### State Management
241
+
242
+ #### .state(initialState)
243
+
244
+ Set initial state values.
245
+
246
+ ```js
247
+ gcanvas({ bg: 'black' })
248
+ .state({ score: 0, lives: 3, level: 1 })
249
+ .scene('game')
250
+ // ...
251
+ ```
252
+
253
+ #### .getState(key)
254
+
255
+ Get a state value.
256
+
257
+ ```js
258
+ const score = game.getState('score');
259
+ ```
260
+
261
+ #### .setState(key, value)
262
+
263
+ Set a state value.
264
+
265
+ ```js
266
+ game.setState('score', game.getState('score') + 100);
267
+ ```
268
+
269
+ ### Events & Lifecycle
270
+
271
+ #### .on(event, handler)
272
+
273
+ Register event handlers.
274
+
275
+ ```js
276
+ // Update loop
277
+ .on('update', (dt, ctx) => {
278
+ // Called every frame
279
+ // dt = delta time, ctx = context object
280
+ })
281
+
282
+ // Keyboard events with key filtering
283
+ .on('keydown:space', (ctx, event) => {
284
+ ctx.refs.player.jump();
285
+ })
286
+
287
+ .on('keydown:escape', (ctx) => {
288
+ ctx.showScene('pause');
289
+ })
290
+
291
+ // Raw events
292
+ .on('click', (ctx, event) => {
293
+ console.log('Clicked at', event.x, event.y);
294
+ })
295
+
296
+ .on('keydown', (ctx, event) => {
297
+ console.log('Key pressed:', event.key);
298
+ })
299
+ ```
300
+
301
+ **Event handler context object:**
302
+ ```js
303
+ {
304
+ refs, // Named object references
305
+ state, // Shared state object
306
+ scenes, // All scenes (as object)
307
+ game, // Underlying Game instance
308
+ width, // Canvas width
309
+ height, // Canvas height
310
+ showScene, // Helper function
311
+ hideScene, // Helper function
312
+ transition // Helper function
313
+ }
314
+ ```
315
+
316
+ ### Plugins & Extensions
317
+
318
+ #### .use(plugin)
319
+
320
+ Use a plugin or composable scene builder function.
321
+
322
+ ```js
323
+ // Plugin function
324
+ const playerModule = (g) => g
325
+ .inScene('game')
326
+ .go({ x: 400, y: 300, name: 'player' })
327
+ .circle({ radius: 25, fill: 'lime' });
328
+
329
+ // Usage
330
+ gcanvas({ bg: 'black' })
331
+ .scene('game')
332
+ .use(playerModule)
333
+ .start();
334
+ ```
335
+
336
+ ### Lifecycle
337
+
338
+ #### .start()
339
+
340
+ Start the game loop.
341
+
342
+ #### .stop()
343
+
344
+ Stop the game loop.
345
+
346
+ #### .restart()
347
+
348
+ Restart the game.
349
+
350
+ ### Accessors
351
+
352
+ | Property | Type | Description |
353
+ |----------|------|-------------|
354
+ | `.game` | `Game` | Underlying Game instance |
355
+ | `.refs` | `Object` | Named object references |
356
+ | `.scenes` | `Map<string, Scene>` | All scenes |
357
+ | `.canvas` | `HTMLCanvasElement` | Canvas element |
358
+ | `.width` | `number` | Canvas width |
359
+ | `.height` | `number` | Canvas height |
360
+
361
+ ---
362
+
363
+ ## FluentScene API
364
+
365
+ Builder for scene operations.
366
+
367
+ ### GameObject Creation
368
+
369
+ #### .go(options)
370
+
371
+ Create a GameObject in this scene.
372
+
373
+ ```js
374
+ .scene('game')
375
+ .go({ x: 100, y: 100, name: 'player', visible: true })
376
+ .circle({ radius: 20 })
377
+ ```
378
+
379
+ **Options:**
380
+ - `x`, `y` - Position (default: 0, 0)
381
+ - `name` - Name for refs lookup
382
+ - `visible` - Initial visibility (default: true)
383
+ - Any custom properties passed to GameObject
384
+
385
+ **Signatures:**
386
+ - `go()` - Plain GameObject at origin
387
+ - `go(options)` - Plain GameObject with options
388
+ - `go(CustomClass)` - Custom GameObject class
389
+ - `go(CustomClass, options)` - Custom class with options
390
+ - `go(options, builderFn)` - With builder callback
391
+ - `go(CustomClass, options, builderFn)` - Custom class with builder
392
+
393
+ #### .group(name, builderFn)
394
+
395
+ Create multiple GOs with a builder function.
396
+
397
+ ```js
398
+ .scene('game')
399
+ .group('enemies', (api) => {
400
+ for (let i = 0; i < 5; i++) {
401
+ api.go({ x: 100 + i * 80, y: 100 })
402
+ .rect({ width: 30, height: 30, fill: 'red' });
403
+ }
404
+ })
405
+ ```
406
+
407
+ ### Layer Management
408
+
409
+ #### .layer(name, zIndex)
410
+
411
+ Create a z-indexed layer within the scene.
412
+
413
+ ```js
414
+ .scene('game')
415
+ .layer('background', -10)
416
+ .go({ x: 400, y: 300 })
417
+ .rect({ width: 800, height: 600, fill: '#333' })
418
+ .endLayer()
419
+ .layer('foreground', 10)
420
+ .go({ x: 400, y: 300 })
421
+ .circle({ radius: 30, fill: 'lime' })
422
+ ```
423
+
424
+ ### Lifecycle Hooks
425
+
426
+ #### .onEnter(handler)
427
+
428
+ Register scene enter callback.
429
+
430
+ ```js
431
+ .scene('game', {
432
+ onEnter: (ctx) => console.log('Entered game scene')
433
+ })
434
+ // Or
435
+ .scene('game')
436
+ .onEnter((ctx) => console.log('Entered game scene'))
437
+ ```
438
+
439
+ #### .onExit(handler)
440
+
441
+ Register scene exit callback.
442
+
443
+ ### Navigation
444
+
445
+ | Method | Returns | Description |
446
+ |--------|---------|-------------|
447
+ | `.scene(name, opts)` | `FluentScene` | Switch to another scene |
448
+ | `.end()` | `FluentGame` | Return to game context |
449
+ | `.start()` | `FluentGame` | Start the game |
450
+ | `.stop()` | `FluentGame` | Stop the game |
451
+ | `.on(event, handler)` | `FluentGame` | Register event (delegates to game) |
452
+ | `.use(plugin)` | `FluentGame` | Use plugin (delegates to game) |
453
+ | `.state(obj)` | `FluentGame` | Set state (delegates to game) |
454
+
455
+ ### Accessors
456
+
457
+ | Property | Type | Description |
458
+ |----------|------|-------------|
459
+ | `.sceneInstance` | `Scene` | Underlying Scene instance |
460
+ | `.refs` | `Object` | Named object references |
461
+ | `.state` | `Object` | Shared state |
462
+ | `.parent` | `FluentGame` | Parent FluentGame |
463
+
464
+ ---
465
+
466
+ ## FluentGO API
467
+
468
+ Builder for GameObject operations.
469
+
470
+ ### Shape Methods
471
+
472
+ All shape methods accept an options object and return `FluentGO` for chaining.
473
+
474
+ #### Basic Shapes
475
+
476
+ ```js
477
+ .circle({ radius: 30, fill: 'red', stroke: 'white', lineWidth: 2 })
478
+ .rect({ width: 100, height: 50, fill: 'blue' })
479
+ .roundRect({ width: 100, height: 50, radius: 10, fill: 'blue' })
480
+ .square({ size: 50, fill: 'green' })
481
+ .triangle({ size: 40, fill: 'yellow' })
482
+ .line({ x2: 100, y2: 50, stroke: 'white', lineWidth: 2 })
483
+ ```
484
+
485
+ #### Complex Shapes
486
+
487
+ ```js
488
+ .star({ points: 5, radius: 30, innerRadius: 15, fill: 'gold' })
489
+ .hexagon({ radius: 25, fill: 'purple' })
490
+ .diamond({ width: 40, height: 60, fill: 'cyan' })
491
+ .heart({ size: 30, fill: 'red' })
492
+ .arc({ radius: 40, startAngle: 0, endAngle: Math.PI, fill: 'orange' })
493
+ .ring({ innerRadius: 20, outerRadius: 40, fill: 'teal' })
494
+ .arrow({ width: 60, height: 20, fill: 'white' })
495
+ .cross({ size: 30, thickness: 8, fill: 'red' })
496
+ .pin({ radius: 15, fill: 'red' })
497
+ .cloud({ width: 80, height: 40, fill: 'white' })
498
+ .poly({ points: [[0,-30], [30,30], [-30,30]], fill: 'lime' })
499
+ ```
500
+
501
+ #### Special Shapes
502
+
503
+ ```js
504
+ .text('Hello World', { font: '24px monospace', fill: 'white' })
505
+ .image('/path/to/image.png', { width: 64, height: 64 })
506
+ .svg('M10 10 L90 10 L50 90 Z', { fill: 'red', scale: 0.5 })
507
+ ```
508
+
509
+ #### Generic Shape
510
+
511
+ ```js
512
+ .add(CustomShapeClass, { /* options */ })
513
+ ```
514
+
515
+ **Common shape options:**
516
+ - `fill` - Fill color (alias for `color`)
517
+ - `stroke` - Stroke color
518
+ - `lineWidth` - Stroke width
519
+ - `opacity` - Shape opacity (0-1)
520
+
521
+ ### Motion Methods
522
+
523
+ Built-in motion presets that animate the GameObject.
524
+
525
+ #### .oscillate(opts)
526
+
527
+ Oscillate a property between min and max values.
528
+
529
+ ```js
530
+ .oscillate({
531
+ prop: 'y', // Property to animate (default: 'y')
532
+ min: -50, // Minimum offset (default: -50)
533
+ max: 50, // Maximum offset (default: 50)
534
+ duration: 2 // Duration in seconds (default: 2)
535
+ })
536
+ ```
537
+
538
+ #### .pulse(opts)
539
+
540
+ Pulse scale between min and max.
541
+
542
+ ```js
543
+ .pulse({
544
+ prop: 'scale', // Property (default: 'scale')
545
+ min: 0.8, // Minimum value (default: 0.8)
546
+ max: 1.2, // Maximum value (default: 1.2)
547
+ duration: 1 // Duration (default: 1)
548
+ })
549
+ ```
550
+
551
+ #### .orbit(opts)
552
+
553
+ Orbit around a center point.
554
+
555
+ ```js
556
+ .orbit({
557
+ centerX: 400, // Orbit center X (default: base position)
558
+ centerY: 300, // Orbit center Y
559
+ radiusX: 100, // X radius (default: 100)
560
+ radiusY: 100, // Y radius (default: 100)
561
+ duration: 3, // Orbit period (default: 3)
562
+ clockwise: true // Direction (default: true)
563
+ })
564
+ ```
565
+
566
+ #### .float(opts)
567
+
568
+ Random wandering motion.
569
+
570
+ ```js
571
+ .float({
572
+ radius: 20, // Float radius (default: 20)
573
+ speed: 0.5, // Float speed (default: 0.5)
574
+ randomness: 0.3, // Randomness factor (default: 0.3)
575
+ duration: 5 // Duration (default: 5)
576
+ })
577
+ ```
578
+
579
+ #### .shake(opts)
580
+
581
+ Shake effect.
582
+
583
+ ```js
584
+ .shake({
585
+ intensity: 5, // Shake intensity (default: 5)
586
+ frequency: 20, // Shake frequency (default: 20)
587
+ decay: 0.9, // Decay factor (default: 0.9)
588
+ duration: 0.5 // Duration (default: 0.5)
589
+ })
590
+ ```
591
+
592
+ #### .bounce(opts)
593
+
594
+ Bouncing motion.
595
+
596
+ ```js
597
+ .bounce({
598
+ height: 100, // Bounce height (default: 100)
599
+ bounces: 3, // Number of bounces (default: 3)
600
+ duration: 2 // Duration (default: 2)
601
+ })
602
+ ```
603
+
604
+ #### .spring(opts)
605
+
606
+ Spring physics motion.
607
+
608
+ #### .spiral(opts)
609
+
610
+ Spiral outward or inward.
611
+
612
+ ```js
613
+ .spiral({
614
+ startRadius: 50, // Starting radius (default: 50)
615
+ endRadius: 150, // Ending radius (default: 150)
616
+ revolutions: 3, // Number of revolutions (default: 3)
617
+ duration: 4 // Duration (default: 4)
618
+ })
619
+ ```
620
+
621
+ #### .pendulum(opts)
622
+
623
+ Pendulum swing motion.
624
+
625
+ ```js
626
+ .pendulum({
627
+ amplitude: 45, // Swing amplitude in degrees (default: 45)
628
+ duration: 2, // Period (default: 2)
629
+ damped: false // Apply damping (default: false)
630
+ })
631
+ ```
632
+
633
+ #### .waypoint(opts)
634
+
635
+ Move between waypoints.
636
+
637
+ ```js
638
+ .waypoint({
639
+ waypoints: [
640
+ { x: 100, y: 100 },
641
+ { x: 300, y: 100 },
642
+ { x: 300, y: 300 },
643
+ { x: 100, y: 300 }
644
+ ],
645
+ speed: 100, // Movement speed (default: 100)
646
+ waitTime: 0 // Wait time at each point (default: 0)
647
+ })
648
+ ```
649
+
650
+ #### .motion(type, opts)
651
+
652
+ Generic motion by type name.
653
+
654
+ ```js
655
+ .motion('oscillate', { prop: 'x', min: -30, max: 30 })
656
+ ```
657
+
658
+ ### Tween
659
+
660
+ #### .tween(props, opts)
661
+
662
+ Tween properties over time.
663
+
664
+ ```js
665
+ .tween({ x: 500, y: 400, opacity: 0.5 }, {
666
+ duration: 1, // Duration in seconds (default: 1)
667
+ easing: 'easeOutQuad', // Easing function (default: 'easeOutQuad')
668
+ delay: 0, // Delay before starting (default: 0)
669
+ onComplete: () => {} // Completion callback
670
+ })
671
+ ```
672
+
673
+ ### Transform Shortcuts
674
+
675
+ ```js
676
+ .pos(x, y) // Set position
677
+ .scale(sx, sy) // Set scale (sy defaults to sx)
678
+ .rotate(degrees) // Set rotation in degrees
679
+ .opacity(value) // Set opacity (0-1)
680
+ .zIndex(value) // Set z-index
681
+ ```
682
+
683
+ ### Child GameObjects
684
+
685
+ #### .child(opts, builderFn)
686
+
687
+ Create a child GameObject.
688
+
689
+ ```js
690
+ .go({ x: 400, y: 300, name: 'parent' })
691
+ .circle({ radius: 50, fill: 'blue' })
692
+ .child({ x: 30, y: 0 }) // Position relative to parent
693
+ .circle({ radius: 10, fill: 'red' })
694
+ .end() // Back to parent
695
+ ```
696
+
697
+ **Signatures:**
698
+ - `child()` - Plain child GO
699
+ - `child(options)` - Plain child with options
700
+ - `child(CustomClass)` - Custom child class
701
+ - `child(CustomClass, options)` - Custom class with options
702
+ - `child(options, builderFn)` - With builder callback
703
+ - `child(CustomClass, options, builderFn)` - Custom class with builder
704
+
705
+ ### Events
706
+
707
+ #### .on(event, handler)
708
+
709
+ Register event handler on this GO.
710
+
711
+ ```js
712
+ .go({ x: 400, y: 300 })
713
+ .circle({ radius: 30 })
714
+ .on('click', (ctx, event) => {
715
+ console.log('Clicked!', ctx.go);
716
+ })
717
+ ```
718
+
719
+ #### .update(fn)
720
+
721
+ Custom update function for this GO.
722
+
723
+ ```js
724
+ .go({ x: 400, y: 300 })
725
+ .circle({ radius: 30 })
726
+ .update((dt, ctx) => {
727
+ ctx.go.rotation += dt * 2;
728
+ })
729
+ ```
730
+
731
+ **Update context:**
732
+ ```js
733
+ {
734
+ go, // This GameObject
735
+ shapes, // Shapes added to this GO
736
+ refs, // Named object references
737
+ state // Shared state
738
+ }
739
+ ```
740
+
741
+ ### Navigation
742
+
743
+ | Method | Returns | Description |
744
+ |--------|---------|-------------|
745
+ | `.end()` | Parent context | Navigate back to parent |
746
+ | `.go(opts)` | `FluentGO` | Create sibling GO |
747
+ | `.scene(name, opts)` | `FluentScene` | Switch to scene |
748
+ | `.start()` | `FluentGame` | Start the game |
749
+
750
+ ### Accessors
751
+
752
+ | Property | Type | Description |
753
+ |----------|------|-------------|
754
+ | `.goInstance` | `GameObject` | Underlying GameObject |
755
+ | `.shapes` | `Array` | Shapes added to this GO |
756
+ | `.refs` | `Object` | Named object references |
757
+ | `.state` | `Object` | Shared state |
758
+
759
+ ---
760
+
761
+ ## FluentLayer API
762
+
763
+ Builder for z-indexed layers within a scene.
764
+
765
+ ### Methods
766
+
767
+ ```js
768
+ .layer('foreground', 10)
769
+ .go({ x: 100, y: 100 })
770
+ .circle({ radius: 20 })
771
+ .visible(true) // Set layer visibility
772
+ .opacity(0.8) // Set layer opacity
773
+ .endLayer() // Return to scene
774
+ ```
775
+
776
+ | Method | Returns | Description |
777
+ |--------|---------|-------------|
778
+ | `.go(opts, builderFn)` | `FluentGO` or `FluentLayer` | Create GO in layer |
779
+ | `.visible(bool)` | `FluentLayer` | Set layer visibility |
780
+ | `.opacity(value)` | `FluentLayer` | Set layer opacity |
781
+ | `.endLayer()` | `FluentScene` | Return to scene |
782
+ | `.end()` | `FluentScene` | Return to scene |
783
+ | `.scene(name, opts)` | `FluentScene` | Switch to scene |
784
+ | `.start()` | `FluentGame` | Start the game |
785
+
786
+ ---
787
+
788
+ ## Sketch Mode API
789
+
790
+ Ultra-simple API for quick creative coding.
791
+
792
+ ### Shapes
793
+
794
+ All shape methods take positional arguments and return the sketch API for chaining.
795
+
796
+ ```js
797
+ sketch(800, 600, 'black')
798
+ .circle(x, y, radius, fill)
799
+ .rect(x, y, width, height, fill)
800
+ .square(x, y, size, fill)
801
+ .star(x, y, points, radius, fill)
802
+ .triangle(x, y, size, fill)
803
+ .hexagon(x, y, radius, fill)
804
+ .line(x1, y1, x2, y2, stroke, lineWidth)
805
+ .ring(x, y, innerRadius, outerRadius, fill)
806
+ .text(content, x, y, { fill, font })
807
+ ```
808
+
809
+ ### Bulk Creation
810
+
811
+ #### .grid(cols, rows, spacing, fn)
812
+
813
+ Create a grid of shapes.
814
+
815
+ ```js
816
+ sketch(800, 600, 'black')
817
+ .grid(10, 10, 80, (api, x, y, col, row) => {
818
+ api.circle(x, y, 20, `hsl(${(col + row) * 20}, 70%, 60%)`);
819
+ })
820
+ .start();
821
+ ```
822
+
823
+ #### .repeat(count, fn)
824
+
825
+ Repeat shape creation.
826
+
827
+ ```js
828
+ sketch(800, 600, 'black')
829
+ .repeat(20, (api, i, total) => {
830
+ const angle = (i / total) * Math.PI * 2;
831
+ const x = 400 + Math.cos(angle) * 200;
832
+ const y = 300 + Math.sin(angle) * 200;
833
+ api.circle(x, y, 15, `hsl(${i * 18}, 70%, 60%)`);
834
+ })
835
+ .start();
836
+ ```
837
+
838
+ #### .radial(cx, cy, radius, count, fn)
839
+
840
+ Create shapes in a circular pattern.
841
+
842
+ ```js
843
+ sketch(800, 600, 'black')
844
+ .radial(400, 300, 150, 12, (api, x, y, angle, i) => {
845
+ api.star(x, y, 5, 20, `hsl(${i * 30}, 70%, 60%)`);
846
+ })
847
+ .start();
848
+ ```
849
+
850
+ ### Lifecycle
851
+
852
+ #### .setup(fn)
853
+
854
+ Register a setup function called once before start.
855
+
856
+ ```js
857
+ .setup((api) => {
858
+ // Initialize shapes
859
+ })
860
+ ```
861
+
862
+ #### .update(fn)
863
+
864
+ Register an update function called every frame.
865
+
866
+ ```js
867
+ .update((dt, ctx) => {
868
+ // ctx.shapes - array of GameObjects
869
+ // ctx.time - elapsed time in seconds
870
+ // ctx.frame - current frame number
871
+ // ctx.width, ctx.height - canvas dimensions
872
+ // ctx.mouse.x, ctx.mouse.y - mouse position
873
+ // ctx.refs - named objects
874
+ // ctx.game - Game instance
875
+ })
876
+ ```
877
+
878
+ #### .start()
879
+
880
+ Start the sketch.
881
+
882
+ ### Properties
883
+
884
+ | Property | Type | Description |
885
+ |----------|------|-------------|
886
+ | `.width` | `number` | Canvas width |
887
+ | `.height` | `number` | Canvas height |
888
+ | `.game` | `FluentGame` | Underlying FluentGame (after start) |
889
+
890
+ ---
891
+
892
+ ## Advanced Patterns
893
+
894
+ ### Class Injection
895
+
896
+ Use custom Scene or GameObject classes with the fluent API.
897
+
898
+ ```js
899
+ // Custom Scene class
900
+ class GameScene extends Scene {
901
+ init() {
902
+ this.spawnTimer = 0;
903
+ }
904
+
905
+ update(dt) {
906
+ super.update(dt);
907
+ this.spawnTimer += dt;
908
+ if (this.spawnTimer > 1) {
909
+ this.spawnEnemy();
910
+ this.spawnTimer = 0;
911
+ }
912
+ }
913
+
914
+ spawnEnemy() { /* ... */ }
915
+ }
916
+
917
+ // Custom GameObject class
918
+ class Player extends GameObject {
919
+ constructor(opts) {
920
+ super(opts);
921
+ this.speed = 200;
922
+ this.health = 100;
923
+ }
924
+
925
+ update(dt) {
926
+ super.update(dt);
927
+ // Custom update logic
928
+ }
929
+ }
930
+
931
+ // Usage
932
+ gcanvas({ bg: 'black' })
933
+ .scene('game', GameScene)
934
+ .go(Player, { x: 400, y: 500, name: 'player' })
935
+ .circle({ radius: 25, fill: 'lime' })
936
+ .start();
937
+ ```
938
+
939
+ ### Composable Scene Modules
940
+
941
+ Create reusable scene builder functions.
942
+
943
+ ```js
944
+ // scenes/player.js
945
+ export const playerModule = (g) => g
946
+ .inScene('game')
947
+ .go({ x: 400, y: 500, name: 'player' })
948
+ .circle({ radius: 25, fill: '#00ff88' })
949
+ .on('update', (ctx) => {
950
+ // Player update logic
951
+ });
952
+
953
+ // scenes/enemies.js
954
+ export const enemiesModule = (g) => g
955
+ .inScene('game')
956
+ .group('enemies', (api) => {
957
+ for (let i = 0; i < 5; i++) {
958
+ api.go({ x: 100 + i * 150, y: 100, name: `enemy_${i}` })
959
+ .rect({ width: 40, height: 40, fill: 'red' });
960
+ }
961
+ });
962
+
963
+ // scenes/ui.js
964
+ export const uiModule = (g) => g
965
+ .scene('ui', { zIndex: 100 })
966
+ .go({ x: 20, y: 20, name: 'scoreText' })
967
+ .text('SCORE: 0', { font: '20px monospace', fill: 'white' });
968
+
969
+ // main.js
970
+ import { gcanvas } from 'gcanvas';
971
+ import { playerModule } from './scenes/player';
972
+ import { enemiesModule } from './scenes/enemies';
973
+ import { uiModule } from './scenes/ui';
974
+
975
+ gcanvas({ bg: 'black' })
976
+ .scene('game')
977
+ .use(playerModule)
978
+ .use(enemiesModule)
979
+ .use(uiModule)
980
+ .on('update', (dt, ctx) => {
981
+ // Main game loop
982
+ })
983
+ .start();
984
+ ```
985
+
986
+ ### State Management
987
+
988
+ ```js
989
+ gcanvas({ bg: 'black' })
990
+ // Initialize state
991
+ .state({
992
+ score: 0,
993
+ lives: 3,
994
+ level: 1,
995
+ paused: false
996
+ })
997
+ .scene('game')
998
+ .go({ x: 20, y: 20, name: 'scoreText' })
999
+ .text('Score: 0', { fill: 'white' })
1000
+ .update((dt, ctx) => {
1001
+ // Update text based on state
1002
+ const score = ctx.state.score;
1003
+ ctx.shapes[0].text = `Score: ${score}`;
1004
+ })
1005
+ .on('update', (dt, ctx) => {
1006
+ // Increment score
1007
+ ctx.state.score += dt * 10;
1008
+ })
1009
+ .on('keydown:p', (ctx) => {
1010
+ ctx.state.paused = !ctx.state.paused;
1011
+ })
1012
+ .start();
1013
+ ```
1014
+
1015
+ ---
1016
+
1017
+ ## Context Object Reference
1018
+
1019
+ Event handlers receive a context object with the following properties:
1020
+
1021
+ ### Game-level context (.on handlers)
1022
+
1023
+ ```js
1024
+ {
1025
+ refs, // Named object references (objects with name prop)
1026
+ state, // Shared state object
1027
+ scenes, // All scenes as { name: Scene }
1028
+ game, // Underlying Game instance
1029
+ width, // Canvas width
1030
+ height, // Canvas height
1031
+ showScene, // (name) => void - Show a scene
1032
+ hideScene, // (name) => void - Hide a scene
1033
+ transition // (from, to, opts) => void - Transition scenes
1034
+ }
1035
+ ```
1036
+
1037
+ ### GO-level context (.update handlers)
1038
+
1039
+ ```js
1040
+ {
1041
+ go, // This GameObject instance
1042
+ shapes, // Array of shapes on this GO
1043
+ refs, // Named object references
1044
+ state // Shared state object
1045
+ }
1046
+ ```
1047
+
1048
+ ### Sketch context (.update handlers)
1049
+
1050
+ ```js
1051
+ {
1052
+ shapes, // Array of GameObjects (one per shape)
1053
+ time, // Elapsed time in seconds
1054
+ frame, // Current frame number
1055
+ width, // Canvas width
1056
+ height, // Canvas height
1057
+ mouse, // { x, y } mouse position
1058
+ refs, // Named objects
1059
+ game // Underlying Game instance
1060
+ }
1061
+ ```
1062
+
1063
+ ---
1064
+
1065
+ ## Related
1066
+
1067
+ - [Game Module](../game/README.md) - Game loop and GameObjects
1068
+ - [Shapes Module](../shapes/README.md) - Shape classes and hierarchy
1069
+ - [Motion Module](../motion/README.md) - Animation functions
1070
+ - [Collision Module](../collision/README.md) - Collision detection
1071
+
1072
+ ## See Also
1073
+
1074
+ - [Fluent API Demo](../../demos/fluent.html) - Interactive examples
1075
+ - [Space Invaders](../../demos/space.html) - Full game using fluent API