@guinetik/gcanvas 1.0.0 → 1.0.2

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 (102) hide show
  1. package/demos/coordinates.html +698 -0
  2. package/demos/cube3d.html +23 -0
  3. package/demos/demos.css +17 -3
  4. package/demos/dino.html +42 -0
  5. package/demos/fluid-simple.html +22 -0
  6. package/demos/fluid.html +37 -0
  7. package/demos/gameobjects.html +626 -0
  8. package/demos/index.html +19 -7
  9. package/demos/js/blob.js +18 -5
  10. package/demos/js/coordinates.js +840 -0
  11. package/demos/js/cube3d.js +789 -0
  12. package/demos/js/dino.js +1420 -0
  13. package/demos/js/fluid-simple.js +253 -0
  14. package/demos/js/fluid.js +527 -0
  15. package/demos/js/gameobjects.js +176 -0
  16. package/demos/js/plane3d.js +256 -0
  17. package/demos/js/platformer.js +1579 -0
  18. package/demos/js/sphere3d.js +229 -0
  19. package/demos/js/sprite.js +473 -0
  20. package/demos/js/tde/accretiondisk.js +65 -12
  21. package/demos/js/tde/blackholescene.js +2 -2
  22. package/demos/js/tde/config.js +2 -2
  23. package/demos/js/tde/index.js +152 -27
  24. package/demos/js/tde/lensedstarfield.js +32 -25
  25. package/demos/js/tde/tdestar.js +78 -98
  26. package/demos/js/tde/tidalstream.js +24 -8
  27. package/demos/plane3d.html +24 -0
  28. package/demos/platformer.html +43 -0
  29. package/demos/sphere3d.html +24 -0
  30. package/demos/sprite.html +18 -0
  31. package/docs/README.md +230 -222
  32. package/docs/api/FluidSystem.md +173 -0
  33. package/docs/concepts/architecture-overview.md +204 -204
  34. package/docs/concepts/coordinate-system.md +384 -0
  35. package/docs/concepts/rendering-pipeline.md +279 -279
  36. package/docs/concepts/shapes-vs-gameobjects.md +187 -0
  37. package/docs/concepts/two-layer-architecture.md +229 -229
  38. package/docs/fluid-dynamics.md +99 -0
  39. package/docs/getting-started/first-game.md +354 -354
  40. package/docs/getting-started/installation.md +175 -157
  41. package/docs/modules/collision/README.md +2 -2
  42. package/docs/modules/fluent/README.md +6 -6
  43. package/docs/modules/game/README.md +303 -303
  44. package/docs/modules/isometric-camera.md +2 -2
  45. package/docs/modules/isometric.md +1 -1
  46. package/docs/modules/painter/README.md +328 -328
  47. package/docs/modules/particle/README.md +3 -3
  48. package/docs/modules/shapes/README.md +221 -221
  49. package/docs/modules/shapes/base/euclidian.md +123 -123
  50. package/docs/modules/shapes/base/shape.md +262 -262
  51. package/docs/modules/shapes/base/transformable.md +243 -243
  52. package/docs/modules/state/README.md +2 -2
  53. package/docs/modules/util/README.md +1 -1
  54. package/docs/modules/util/camera3d.md +3 -3
  55. package/docs/modules/util/scene3d.md +1 -1
  56. package/package.json +3 -1
  57. package/readme.md +19 -5
  58. package/src/collision/collision.js +75 -0
  59. package/src/game/game.js +11 -5
  60. package/src/game/index.js +2 -1
  61. package/src/game/objects/index.js +3 -0
  62. package/src/game/objects/platformer-scene.js +411 -0
  63. package/src/game/objects/scene.js +14 -0
  64. package/src/game/objects/sprite.js +529 -0
  65. package/src/game/pipeline.js +20 -16
  66. package/src/game/systems/FluidSystem.js +835 -0
  67. package/src/game/systems/index.js +11 -0
  68. package/src/game/ui/button.js +39 -18
  69. package/src/game/ui/cursor.js +14 -0
  70. package/src/game/ui/fps.js +12 -4
  71. package/src/game/ui/index.js +2 -0
  72. package/src/game/ui/stepper.js +549 -0
  73. package/src/game/ui/theme.js +123 -0
  74. package/src/game/ui/togglebutton.js +9 -3
  75. package/src/game/ui/tooltip.js +11 -4
  76. package/src/io/input.js +75 -45
  77. package/src/io/mouse.js +44 -19
  78. package/src/io/touch.js +35 -12
  79. package/src/math/fluid.js +507 -0
  80. package/src/math/index.js +2 -0
  81. package/src/mixins/anchor.js +17 -7
  82. package/src/motion/tweenetik.js +16 -0
  83. package/src/shapes/cube3d.js +599 -0
  84. package/src/shapes/index.js +3 -0
  85. package/src/shapes/plane3d.js +687 -0
  86. package/src/shapes/sphere3d.js +75 -6
  87. package/src/util/camera2d.js +315 -0
  88. package/src/util/camera3d.js +218 -12
  89. package/src/util/index.js +1 -0
  90. package/src/webgl/shaders/plane-shaders.js +332 -0
  91. package/src/webgl/shaders/sphere-shaders.js +4 -2
  92. package/types/fluent.d.ts +361 -0
  93. package/types/game.d.ts +303 -0
  94. package/types/index.d.ts +144 -5
  95. package/types/math.d.ts +361 -0
  96. package/types/motion.d.ts +271 -0
  97. package/types/particle.d.ts +373 -0
  98. package/types/shapes.d.ts +107 -9
  99. package/types/util.d.ts +353 -0
  100. package/types/webgl.d.ts +109 -0
  101. package/disk_example.png +0 -0
  102. package/tde.png +0 -0
@@ -2,15 +2,12 @@ import { GameObject, Sphere3D } from "../../../src/index.js";
2
2
  import { polarToCartesian } from "../../../src/math/gr.js";
3
3
  import { CONFIG } from "./config.js";
4
4
 
5
- // Star shader configuration
6
- const STAR_SHADER_CONFIG = {
7
- useShader: true,
8
- shaderType: "star",
9
- shaderUniforms: {
10
- uStarColor: [1.0, 0.85, 0.3], // Golden yellow
11
- uTemperature: 5500, // K (slightly cooler than Sun)
12
- uActivityLevel: 0.75, // Moderate surface activity
13
- },
5
+ // Performance tuning: reduce update frequency for expensive operations
6
+ const PERF_CONFIG = {
7
+ geometryUpdateThreshold: 0.02, // Only regenerate geometry if radius changes by 2%
8
+ uniformUpdateInterval: 2, // Update shader uniforms every N frames
9
+ breathingEnabled: true, // Toggle breathing effect
10
+ stressColorEnabled: true, // Toggle dynamic color shifts
14
11
  };
15
12
 
16
13
  export class Star extends GameObject {
@@ -48,6 +45,11 @@ export class Star extends GameObject {
48
45
  this.tidalProgress = 0; // External tidal progress from FSM (0-1)
49
46
  this.tidalFlare = 0; // 0-1, sudden brightness burst at disruption start
50
47
  this.tidalWobble = 0; // 0-1, violent geometry wobble during trauma
48
+
49
+ // Performance optimization state
50
+ this._frameCount = 0;
51
+ this._lastGeometryRadius = 0;
52
+ this._cachedUniforms = null;
51
53
  }
52
54
 
53
55
  init() {
@@ -93,109 +95,73 @@ export class Star extends GameObject {
93
95
  const massRatio = this.mass / this.initialMass;
94
96
 
95
97
  // === NON-LINEAR SIZE COLLAPSE ===
96
- // Star resists at first (internal pressure), then collapses rapidly
97
- // Use a power curve: slow start, rapid end
98
- const collapseProgress = 1 - massRatio; // 0 = full, 1 = gone
99
- const resistanceCurve = Math.pow(collapseProgress, 0.5); // sqrt = resists early
100
- const effectiveMassRatio = 1 - resistanceCurve;
98
+ // Use sqrt for resistance curve (star resists early, then collapses)
99
+ const collapseProgress = 1 - massRatio;
100
+ const effectiveMassRatio = 1 - Math.sqrt(collapseProgress);
101
101
 
102
102
  // Base radius with non-linear collapse
103
103
  this.currentRadius = this.baseRadius * Math.max(0.05, effectiveMassRatio);
104
104
 
105
- // Don't update geometry if star is consumed
105
+ // Don't update if star is consumed
106
106
  if (this.currentRadius <= 0 || this.mass <= 0) {
107
107
  return;
108
108
  }
109
109
 
110
- // === TIDAL STRETCH (Spaghettification) ===
111
- // Create comet/teardrop shape pointed toward black hole
112
- const dist = Math.sqrt(this.x * this.x + (this.z || 0) * (this.z || 0)) || 1;
110
+ // === TIDAL STRETCH (Simplified) ===
111
+ const zVal = this.z || 0;
112
+ const distSq = this.x * this.x + zVal * zVal;
113
+ const dist = Math.sqrt(distSq) || 1;
114
+ const invDist = 1 / dist;
113
115
 
114
116
  // Direction toward black hole (unit vector)
115
- let dirX = -this.x / dist;
116
- let dirZ = -(this.z || 0) / dist;
117
+ const dirX = -this.x * invDist;
118
+ const dirZ = -zVal * invDist;
117
119
 
118
120
  // Proximity factor: closer to BH = more stretch
119
- let proximityFactor = Math.max(0, 1 - dist / this.initialOrbitalRadius);
121
+ const proximityFactor = Math.max(0, 1 - dist / this.initialOrbitalRadius);
120
122
 
121
- // Calculate stretch amount based on phase and proximity
123
+ // Simplified stretch calculation
122
124
  if (collapseProgress > 0.8) {
123
- // Very late stage - reduce stretch as star becomes tiny
124
125
  this.tidalStretch = (1 - collapseProgress) * 2;
125
126
  } else {
126
- // Main deformation: builds with tidalProgress and proximity
127
- // tidalProgress is driven by FSM state (0 in approach, ramps in stretch/disrupt)
128
- const baseStretch = this.tidalProgress * 1.2; // Up to 1.2 stretch
129
- const proximityBoost = proximityFactor * 0.5; // Extra stretch when close
130
-
131
- this.tidalStretch = baseStretch + proximityBoost;
132
- this.tidalStretch = Math.min(1.8, this.tidalStretch); // Cap at 1.8
127
+ this.tidalStretch = Math.min(1.8, this.tidalProgress * 1.2 + proximityFactor * 0.5);
133
128
  }
134
129
 
135
- // === BREATHING (Slow, ominous expansion/contraction) ===
136
- // Very slow rhythm - like a dying star's final gasps
137
- // No rapid bouncing - this should feel cosmic, not cartoonish
138
- const breathingAmp = 0.03 * (1 - collapseProgress * 0.5); // Subtle, weakens as disrupted
139
- const breathing = Math.sin(this.pulsationPhase) * breathingAmp;
140
-
141
- // Apply breathing to radius (very subtle)
142
- this.currentRadius *= (1 + breathing);
130
+ // === BREATHING (Optional, can be disabled for performance) ===
131
+ if (PERF_CONFIG.breathingEnabled) {
132
+ const breathingAmp = 0.03 * (1 - collapseProgress * 0.5);
133
+ this.currentRadius *= (1 + Math.sin(this.pulsationPhase) * breathingAmp);
134
+ }
143
135
 
144
- // === STRESS LEVEL ===
145
- // Combines proximity and mass loss - drives surface chaos
146
- // Use power curve so stress stays LOW for most of disruption, then ramps up sharply
147
- // This gives more time to see the red-orange star with surface chaos
148
- const rawStress = proximityFactor * 0.4 + collapseProgress * 0.6; // Reduced weights
149
- // Power of 3 = stays low longer, ramps up sharply at the end
150
- this.stressLevel = Math.min(1, Math.pow(rawStress, 2.5));
136
+ // === STRESS LEVEL (Simplified power curve) ===
137
+ const rawStress = proximityFactor * 0.4 + collapseProgress * 0.6;
138
+ this.stressLevel = Math.min(1, rawStress * rawStress * rawStress); // Cubic approximation
151
139
 
152
140
  // === ACTIVITY & ROTATION ===
153
- const activityLevel = 0.3 + this.stressLevel * 0.7; // 0.3 -> 1.0
154
-
155
- // Angular momentum conservation: shrinking = faster spin
141
+ const activityLevel = 0.3 + this.stressLevel * 0.7;
156
142
  const baseRotationSpeed = CONFIG.star.rotationSpeed ?? 0.5;
157
- const spinUpFactor = 1 / Math.max(0.2, effectiveMassRatio); // Inverse of size
158
- const rotationSpeed = Math.min(10, baseRotationSpeed * spinUpFactor);
159
-
160
- // === COLOR SHIFT ===
161
- // Start deep red-orange, transition through orange → yellow → white
162
- // This lets us see the tidal chaos on a colorful surface before brightening
163
- //
164
- // Phase 1 (stress 0-0.5): Deep red-orange, surface chaos building
165
- // Phase 2 (stress 0.5-0.8): Shift to orange-yellow, intense activity
166
- // Phase 3 (stress 0.8-1.0): Rapid shift to white-hot, death throes
167
-
168
- // Temperature increases with stress (tidal heating is real physics!)
169
- const tempShift = this.stressLevel * this.stressLevel * 2500; // Up to +2500K at max stress
170
- const temperature = (CONFIG.star.temperature ?? 3800) + tempShift;
171
-
172
- // Color transition: red-orange → orange → yellow → white
173
- // R stays high, G increases with stress, B only increases late
174
- let r = 1.0;
175
- let g, b;
176
-
177
- if (this.stressLevel < 0.5) {
178
- // Phase 1: Deep red-orange → orange (stress 0-0.5)
179
- const t = this.stressLevel * 2; // 0 to 1 over this phase
180
- g = 0.35 + t * 0.25; // 0.35 → 0.6
181
- b = 0.15 + t * 0.1; // 0.15 → 0.25
182
- } else if (this.stressLevel < 0.8) {
183
- // Phase 2: Orange → yellow-orange (stress 0.5-0.8)
184
- const t = (this.stressLevel - 0.5) / 0.3; // 0 to 1
185
- g = 0.6 + t * 0.2; // 0.6 → 0.8
186
- b = 0.25 + t * 0.1; // 0.25 → 0.35
143
+ const rotationSpeed = Math.min(10, baseRotationSpeed / Math.max(0.2, effectiveMassRatio));
144
+
145
+ // === COLOR SHIFT (Simplified linear interpolation) ===
146
+ let r = 1.0, g, b;
147
+ const stress = this.stressLevel;
148
+
149
+ if (PERF_CONFIG.stressColorEnabled) {
150
+ // Simplified color: lerp from red-orange to white based on stress
151
+ g = 0.35 + stress * 0.6; // 0.35 0.95
152
+ b = 0.15 + stress * 0.7; // 0.15 0.85
187
153
  } else {
188
- // Phase 3: Yellow-orange → white-hot (stress 0.8-1.0)
189
- const t = (this.stressLevel - 0.8) / 0.2; // 0 to 1
190
- g = 0.8 + t * 0.15; // 0.8 → 0.95
191
- b = 0.35 + t * 0.5; // 0.35 → 0.85 (rapid blue increase = white)
154
+ g = 0.5;
155
+ b = 0.2;
192
156
  }
193
157
 
194
158
  const stressColor = [r, g, b];
195
-
196
- // Expose current color for particle emission
197
159
  this.currentColor = stressColor;
198
160
 
161
+ // Temperature calculation
162
+ const temperature = (CONFIG.star.temperature ?? 3800) + stress * stress * 2500;
163
+
164
+ // === VISUAL UPDATE ===
199
165
  if (!this.visual) {
200
166
  this.visual = new Sphere3D(this.currentRadius, {
201
167
  color: CONFIG.star.color,
@@ -215,23 +181,37 @@ export class Star extends GameObject {
215
181
  uTidalWobble: this.tidalWobble,
216
182
  },
217
183
  });
184
+ this._lastGeometryRadius = this.currentRadius;
218
185
  } else {
219
186
  this.visual.radius = this.currentRadius;
220
- if (this.visual.useShader) {
221
- this.visual.setShaderUniforms({
222
- uStarColor: stressColor,
223
- uTemperature: temperature,
224
- uActivityLevel: activityLevel,
225
- uRotationSpeed: rotationSpeed,
226
- uTidalStretch: this.tidalStretch,
227
- uStretchDirX: dirX,
228
- uStretchDirZ: dirZ,
229
- uStressLevel: this.stressLevel,
230
- uTidalFlare: this.tidalFlare,
231
- uTidalWobble: this.tidalWobble,
232
- });
187
+
188
+ // Only update shader uniforms every N frames
189
+ this._frameCount++;
190
+ if (this._frameCount >= PERF_CONFIG.uniformUpdateInterval) {
191
+ this._frameCount = 0;
192
+
193
+ if (this.visual.useShader) {
194
+ this.visual.setShaderUniforms({
195
+ uStarColor: stressColor,
196
+ uTemperature: temperature,
197
+ uActivityLevel: activityLevel,
198
+ uRotationSpeed: rotationSpeed,
199
+ uTidalStretch: this.tidalStretch,
200
+ uStretchDirX: dirX,
201
+ uStretchDirZ: dirZ,
202
+ uStressLevel: this.stressLevel,
203
+ uTidalFlare: this.tidalFlare,
204
+ uTidalWobble: this.tidalWobble,
205
+ });
206
+ }
207
+ }
208
+
209
+ // Only regenerate geometry if radius changed significantly
210
+ const radiusChange = Math.abs(this.currentRadius - this._lastGeometryRadius) / this._lastGeometryRadius;
211
+ if (radiusChange > PERF_CONFIG.geometryUpdateThreshold) {
212
+ this.visual._generateGeometry();
213
+ this._lastGeometryRadius = this.currentRadius;
233
214
  }
234
- this.visual._generateGeometry();
235
215
  }
236
216
  }
237
217
 
@@ -13,8 +13,8 @@ import { applyGravitationalLensing } from "../../../src/math/gr.js";
13
13
  // Stream-specific config
14
14
  const STREAM_CONFIG = {
15
15
  gravity: 120000, // Strong gravity (linear falloff G/r)
16
- maxParticles: 5000,
17
- particleLifetime: 12, // Seconds - long lifetime so particles can orbit the BH
16
+ maxParticles: 10000,
17
+ particleLifetime: 20, // Seconds - long lifetime so particles can orbit the BH
18
18
 
19
19
  // Velocity inheritance - how much of star's velocity particles get
20
20
  // Lower = particles emit more "from" the star, not ahead of it
@@ -42,7 +42,7 @@ const STREAM_CONFIG = {
42
42
 
43
43
  // Particle size
44
44
  sizeMin: 1,
45
- sizeMax: 3,
45
+ sizeMax: 1.2,
46
46
 
47
47
  // Gravitational lensing (visual effect)
48
48
  // These are multipliers relative to the BH's current radius
@@ -259,6 +259,12 @@ export class TidalStream extends GameObject {
259
259
  // Build render list with projection
260
260
  const renderList = [];
261
261
 
262
+ // Project black hole position once for all particles
263
+ // This is crucial when camera has moved (e.g., following the star)
264
+ const bhProjected = this.camera.project(0, 0, 0);
265
+ const bhScreenX = bhProjected.x;
266
+ const bhScreenY = bhProjected.y;
267
+
262
268
  // Young particles stay invisible (appear to emerge from star)
263
269
  const fadeInTime = 0.05; // seconds before particles become visible
264
270
  const fadeInDuration = 0.1; // seconds to fade from invisible to full opacity
@@ -299,24 +305,34 @@ export class TidalStream extends GameObject {
299
305
  let screenY = projected.y;
300
306
 
301
307
  if (STREAM_CONFIG.lensing.enabled && this.bhRadius > 0) {
308
+ // Calculate particle position RELATIVE to black hole's screen position
309
+ const relX = screenX - bhScreenX;
310
+ const relY = screenY - bhScreenY;
311
+
302
312
  const effectRadius = this.bhRadius * STREAM_CONFIG.lensing.effectRadiusMult;
303
313
  const strength = this.bhRadius * STREAM_CONFIG.lensing.strengthMult;
304
314
  const minDist = this.bhRadius * STREAM_CONFIG.lensing.minDistanceMult;
305
315
 
316
+ // Apply lensing in BH-relative space (lensing curves toward origin)
306
317
  const lensed = applyGravitationalLensing(
307
- screenX, screenY,
318
+ relX, relY,
308
319
  effectRadius,
309
320
  strength,
310
321
  STREAM_CONFIG.lensing.falloff,
311
322
  minDist
312
323
  );
313
- screenX = lensed.x;
314
- screenY = lensed.y;
324
+
325
+ // Transform back to screen space
326
+ screenX = lensed.x + bhScreenX;
327
+ screenY = lensed.y + bhScreenY;
315
328
  }
316
329
 
317
330
  // Check if particle is visually inside the black hole
318
- const screenDist = Math.sqrt(screenX * screenX + screenY * screenY);
319
- const insideBH = screenDist < this.bhRadius;
331
+ // Use pre-calculated BH screen position
332
+ const dxBH = screenX - bhScreenX;
333
+ const dyBH = screenY - bhScreenY;
334
+ const screenDistFromBH = Math.sqrt(dxBH * dxBH + dyBH * dyBH);
335
+ const insideBH = screenDistFromBH < this.bhRadius;
320
336
 
321
337
  // Screen position = center + lensed offset
322
338
  renderList.push({
@@ -0,0 +1,24 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Plane3D Showcase - GCanvas</title>
7
+ <link rel="stylesheet" href="demos.css" />
8
+ <script src="./js/info-toggle.js"></script>
9
+ </head>
10
+ <body>
11
+ <div id="info">
12
+ <strong>Plane3D Showcase</strong> — 3D plane rendering modes<br/>
13
+ <span style="color:#CCC">
14
+ <li>Solid Color (Canvas 2D rendering)</li>
15
+ <li>Gradient (WebGL shader)</li>
16
+ <li>Grid (WebGL shader)</li>
17
+ <li>Drag to rotate camera</li>
18
+ <li>Double-click to reset view</li>
19
+ </span>
20
+ </div>
21
+ <canvas id="game"></canvas>
22
+ <script type="module" src="./js/plane3d.js"></script>
23
+ </body>
24
+ </html>
@@ -0,0 +1,43 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Tron Platformer - GCanvas Demo</title>
7
+ <link rel="stylesheet" href="demos.css" />
8
+ <script src="./js/info-toggle.js"></script>
9
+ <style>
10
+ /* Override for full immersion */
11
+ body {
12
+ background: #000;
13
+ }
14
+
15
+ canvas#game {
16
+ width: 100vw;
17
+ height: 100vh;
18
+ }
19
+
20
+ #info {
21
+ background: rgba(0, 0, 0, 0.9);
22
+ border-bottom: 1px solid #00ff00;
23
+ }
24
+
25
+ #info strong {
26
+ color: #00ff00;
27
+ }
28
+ </style>
29
+ </head>
30
+ <body>
31
+ <div id="info">
32
+ <strong>NETRUNNER.exe</strong> — platformer demo<br/>
33
+ <span style="color:#666">
34
+ <li>ARROWS / WASD: move left/right</li>
35
+ <li>SPACE / UP / W: jump</li>
36
+ <li>stomp enemies • collect data orbs</li>
37
+ <li>reach the portal to complete the level</li>
38
+ </span>
39
+ </div>
40
+ <canvas id="game"></canvas>
41
+ <script type="module" src="./js/platformer.js"></script>
42
+ </body>
43
+ </html>
@@ -0,0 +1,24 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Sphere3D Showcase - GCanvas</title>
7
+ <link rel="stylesheet" href="demos.css" />
8
+ <script src="./js/info-toggle.js"></script>
9
+ </head>
10
+ <body>
11
+ <div id="info">
12
+ <strong>Sphere3D Showcase</strong> — 3D sphere rendering modes<br/>
13
+ <span style="color:#CCC">
14
+ <li>Solid Color (Canvas 2D rendering)</li>
15
+ <li>Gas Giant (WebGL shader with bands)</li>
16
+ <li>Star (WebGL shader with glow)</li>
17
+ <li>Drag to rotate camera</li>
18
+ <li>Double-click to reset view</li>
19
+ </span>
20
+ </div>
21
+ <canvas id="game"></canvas>
22
+ <script type="module" src="./js/sphere3d.js"></script>
23
+ </body>
24
+ </html>
@@ -0,0 +1,18 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+
4
+ <head>
5
+ <meta charset="UTF-8" />
6
+ <title>Sprite Timeline Demo</title>
7
+ <link rel="stylesheet" href="demos.css" />
8
+ </head>
9
+
10
+ <body>
11
+ <div id="info">
12
+ <strong>Sprite Demo</strong> - Demonstrates the Sprite class, a MovieClip-style GameObject with frame-by-frame timeline animation. Each Sprite contains a collection of Shapes as frames, with timeline controls including play(), pause(), stop(), goto(), gotoAndStop(), gotoAndPlay(), and rewind(). This showcases how to create frame-by-frame animations similar to Adobe Flash MovieClips within the gcanvas engine's abstractions.
13
+ </div>
14
+ <canvas id="game"></canvas>
15
+ <script type="module" src="./js/sprite.js"></script>
16
+ </body>
17
+
18
+ </html>