nova64 0.2.4 → 0.2.6

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 (140) hide show
  1. package/README.md +25 -8
  2. package/bin/nova64.js +165 -0
  3. package/dist/assets/console-CY_kygm3.js +14 -0
  4. package/dist/assets/console-CY_kygm3.js.map +1 -0
  5. package/dist/assets/main-l0sNRNKZ.js.map +1 -0
  6. package/dist/assets/sky/studio/nx.png +0 -0
  7. package/dist/assets/sky/studio/ny.png +0 -0
  8. package/dist/assets/sky/studio/nz.png +0 -0
  9. package/dist/assets/sky/studio/px.png +0 -0
  10. package/dist/assets/sky/studio/py.png +0 -0
  11. package/dist/assets/sky/studio/pz.png +0 -0
  12. package/dist/assets/vanilla-Dcuy32gi.js +2 -0
  13. package/dist/assets/vanilla-Dcuy32gi.js.map +1 -0
  14. package/dist/console.html +899 -0
  15. package/dist/docs/BENCHMARK.md +77 -0
  16. package/dist/docs/CHEATSHEET.md +255 -0
  17. package/dist/docs/EFFECTS_API_GUIDE.md +577 -0
  18. package/dist/docs/EFFECTS_QUICK_REFERENCE.md +331 -0
  19. package/dist/docs/FONT_CHARACTER_REFERENCE.md +219 -0
  20. package/dist/docs/FREE_GLB_ASSETS.md +330 -0
  21. package/dist/docs/FULLSCREEN_BUTTON_FEATURE.md +296 -0
  22. package/dist/docs/GAMEPAD_SUPPORT.md +348 -0
  23. package/dist/docs/GAME_IMPROVEMENTS.md +278 -0
  24. package/dist/docs/GAME_QUALITY_STATUS.md +300 -0
  25. package/dist/docs/MIGRATION_GUIDE.md +553 -0
  26. package/dist/docs/NOVA64_3D_API.md +356 -0
  27. package/dist/docs/NOVA64_API_REFERENCE.md +1406 -0
  28. package/dist/docs/NOVA64_UI_API.md +503 -0
  29. package/dist/docs/UI_SYSTEM_SUMMARY.md +445 -0
  30. package/dist/docs/VOXEL_ENGINE_GUIDE.md +662 -0
  31. package/dist/docs/VOXEL_QUICK_REFERENCE.md +386 -0
  32. package/dist/docs/api-3d.html +750 -0
  33. package/dist/docs/api-effects.html +385 -0
  34. package/dist/docs/api-improvements.md +121 -0
  35. package/dist/docs/api-skybox.html +407 -0
  36. package/dist/docs/api-sprites.html +321 -0
  37. package/dist/docs/api-voxel.html +337 -0
  38. package/dist/docs/api.html +543 -0
  39. package/dist/docs/assets.html +306 -0
  40. package/dist/docs/audio.html +340 -0
  41. package/dist/docs/blogs.html +286 -0
  42. package/dist/docs/collision.html +316 -0
  43. package/dist/docs/console.html +247 -0
  44. package/dist/docs/editor.html +297 -0
  45. package/dist/docs/font.html +247 -0
  46. package/dist/docs/framebuffer.html +247 -0
  47. package/dist/docs/fullscreen-button.html +297 -0
  48. package/dist/docs/gpu-systems.html +247 -0
  49. package/dist/docs/index.html +580 -0
  50. package/dist/docs/input.html +491 -0
  51. package/dist/docs/physics.html +311 -0
  52. package/dist/docs/screens.html +311 -0
  53. package/dist/docs/storage.html +311 -0
  54. package/dist/docs/textinput.html +332 -0
  55. package/dist/docs/ui.html +488 -0
  56. package/dist/examples/3d-advanced/code.js +695 -0
  57. package/dist/examples/adventure-comic-3d/code.js +342 -0
  58. package/dist/examples/audio-lab/code.js +150 -0
  59. package/dist/examples/boids-flocking/code.js +270 -0
  60. package/dist/examples/crystal-cathedral-3d/code.js +706 -0
  61. package/dist/examples/cyberpunk-city-3d/code.js +1383 -0
  62. package/dist/examples/demoscene/README.md +192 -0
  63. package/dist/examples/demoscene/code.js +1081 -0
  64. package/dist/examples/demoscene/meta.json +21 -0
  65. package/dist/examples/dungeon-crawler-3d/code.js +1117 -0
  66. package/dist/examples/f-zero-nova-3d/code.js +865 -0
  67. package/dist/examples/f-zero-nova-3d/code_old.js +1555 -0
  68. package/dist/examples/fps-demo-3d/code.js +744 -0
  69. package/dist/examples/game-of-life-3d/code.js +338 -0
  70. package/dist/examples/generative-art/code.js +632 -0
  71. package/dist/examples/hello-3d/code.js +325 -0
  72. package/dist/examples/hello-skybox/code.js +183 -0
  73. package/dist/examples/hello-world/code.js +19 -0
  74. package/dist/examples/input-showcase/code.js +109 -0
  75. package/dist/examples/instancing-demo/code.js +315 -0
  76. package/dist/examples/minecraft-demo/code.js +387 -0
  77. package/dist/examples/model-viewer-3d/code.js +114 -0
  78. package/dist/examples/mystical-realm-3d/code.js +1203 -0
  79. package/dist/examples/nature-explorer-3d/code.js +1318 -0
  80. package/dist/examples/particles-demo/code.js +522 -0
  81. package/dist/examples/pbr-showcase/code.js +140 -0
  82. package/dist/examples/physics-demo-3d/code.js +948 -0
  83. package/dist/examples/screen-demo/code.js +267 -0
  84. package/dist/examples/shooter-demo-3d/code.js +1286 -0
  85. package/dist/examples/space-combat-3d/IMPLEMENTATION_SUMMARY.md +109 -0
  86. package/dist/examples/space-combat-3d/README.md +135 -0
  87. package/dist/examples/space-combat-3d/code.js +1332 -0
  88. package/dist/examples/space-harrier-3d/code.js +923 -0
  89. package/dist/examples/star-fox-nova-3d/code.js +1116 -0
  90. package/dist/examples/star-fox-nova-3d/code_backup.js +410 -0
  91. package/dist/examples/star-fox-nova-3d/code_broken.js +1821 -0
  92. package/dist/examples/storage-quest/code.js +209 -0
  93. package/dist/examples/strider-demo-3d/IMPROVEMENT_OPTIONS.md +285 -0
  94. package/dist/examples/strider-demo-3d/cache-test.html +132 -0
  95. package/dist/examples/strider-demo-3d/code-fixed.js +582 -0
  96. package/dist/examples/strider-demo-3d/code-old.js +1537 -0
  97. package/dist/examples/strider-demo-3d/code.js +1462 -0
  98. package/dist/examples/strider-demo-3d/code.js.bak2 +1169 -0
  99. package/dist/examples/strider-demo-3d/fix-game.sh +53 -0
  100. package/dist/examples/super-plumber-64/README.md +128 -0
  101. package/dist/examples/super-plumber-64/code.js +1185 -0
  102. package/dist/examples/super-plumber-64/index.html +88 -0
  103. package/dist/examples/test-2d-overlay/code.js +32 -0
  104. package/dist/examples/test-font/code.js +51 -0
  105. package/dist/examples/test-minimal/code.js +21 -0
  106. package/dist/examples/ui-demo/code.js +306 -0
  107. package/dist/examples/wing-commander-space/README.md +180 -0
  108. package/dist/examples/wing-commander-space/code.js +1285 -0
  109. package/dist/examples/wizardry-3d/CHANGELOG.md +366 -0
  110. package/dist/examples/wizardry-3d/code.js +3928 -0
  111. package/dist/index.html +666 -0
  112. package/dist/os9-shell/assets/index-DIHfrTaW.css +1 -0
  113. package/dist/os9-shell/assets/index-KchE_ngx.js +483 -0
  114. package/dist/os9-shell/assets/index-KchE_ngx.js.map +1 -0
  115. package/dist/os9-shell/index.html +23 -0
  116. package/dist/os9-shell/nova-icon.svg +12 -0
  117. package/index.html +6 -1
  118. package/package.json +37 -32
  119. package/public/assets/sky/studio/nx.png +0 -0
  120. package/public/assets/sky/studio/ny.png +0 -0
  121. package/public/assets/sky/studio/nz.png +0 -0
  122. package/public/assets/sky/studio/px.png +0 -0
  123. package/public/assets/sky/studio/py.png +0 -0
  124. package/public/assets/sky/studio/pz.png +0 -0
  125. package/public/os9-shell/assets/index-KchE_ngx.js +483 -0
  126. package/public/os9-shell/assets/index-KchE_ngx.js.map +1 -0
  127. package/public/os9-shell/index.html +10 -1
  128. package/runtime/api-2d.js +301 -21
  129. package/runtime/api-3d/pbr.js +45 -1
  130. package/runtime/api-3d.js +1 -0
  131. package/runtime/api-effects.js +90 -3
  132. package/runtime/api-gameutils.js +476 -0
  133. package/runtime/api-generative.js +610 -0
  134. package/runtime/api-skybox.js +54 -0
  135. package/runtime/api-voxel.js +139 -28
  136. package/runtime/gpu-threejs.js +13 -9
  137. package/runtime/ui.js +2 -2
  138. package/src/main.js +24 -1
  139. package/public/os9-shell/assets/index-B1Uvacma.js +0 -32825
  140. package/public/os9-shell/assets/index-B1Uvacma.js.map +0 -1
@@ -0,0 +1,1406 @@
1
+ # 🎮 NOVA64 API REFERENCE
2
+
3
+ **Version:** 0.2.5
4
+ **Date:** March 20, 2026
5
+ **Resolution:** 640×360 pixels
6
+ **Target:** Nintendo 64 / PlayStation aesthetic
7
+
8
+ ---
9
+
10
+ ## 📚 Table of Contents
11
+
12
+ 1. [Getting Started](#getting-started)
13
+ 2. [Core 2D API](#core-2d-api)
14
+ 3. [Color System](#color-system)
15
+ 4. [Input System](#input-system)
16
+ 5. [3D Graphics API](#3d-graphics-api)
17
+ 6. [Visual Effects API](#visual-effects-api)
18
+ 7. [Voxel Engine API](#voxel-engine-api)
19
+ 8. [UI System](#ui-system)
20
+ 9. [Audio System](#audio-system)
21
+ 10. [Utility Functions](#utility-functions)
22
+
23
+ ---
24
+
25
+ ## 🚀 Getting Started
26
+
27
+ ### Basic Game Structure
28
+
29
+ Every Nova64 game follows this pattern:
30
+
31
+ ```javascript
32
+ // Initialization - runs once
33
+ export function init() {
34
+ // Setup game state, load assets, initialize objects
35
+ console.log('🎮 Game initialized!');
36
+ }
37
+
38
+ // Update - runs every frame (~60 FPS)
39
+ export function update(dt) {
40
+ // dt = delta time in seconds (typically 0.016)
41
+ // Handle input, update physics, game logic
42
+ }
43
+
44
+ // Draw - runs every frame after update
45
+ export function draw() {
46
+ // Render 2D graphics, UI, text
47
+ // 3D graphics are auto-rendered by GPU
48
+ }
49
+ ```
50
+
51
+ ### Screen Resolution
52
+
53
+ - **Internal:** 640×360 pixels (16:9 aspect ratio)
54
+ - **Scaling:** Automatically scales to fit browser window
55
+ - **Retro Look:** Pixel-perfect rendering with optional CRT effects
56
+
57
+ ---
58
+
59
+ ## 🎨 Core 2D API
60
+
61
+ ### Drawing Functions
62
+
63
+ #### `cls(color?)`
64
+
65
+ Clear the screen with a color.
66
+
67
+ ```javascript
68
+ cls(); // Clear to black
69
+ cls(rgba8(0, 0, 0, 255)); // Clear to black
70
+ cls(rgba8(20, 40, 80, 255)); // Clear to dark blue
71
+ ```
72
+
73
+ #### `pset(x, y, color)`
74
+
75
+ Set a single pixel.
76
+
77
+ ```javascript
78
+ pset(100, 100, rgba8(255, 0, 0, 255)); // Red pixel at (100, 100)
79
+ ```
80
+
81
+ #### `line(x0, y0, x1, y1, color)`
82
+
83
+ Draw a line between two points.
84
+
85
+ ```javascript
86
+ line(0, 0, 640, 360, rgba8(255, 255, 255, 255)); // Diagonal white line
87
+ ```
88
+
89
+ #### `rect(x, y, width, height, color, fill?)`
90
+
91
+ Draw a rectangle.
92
+
93
+ ```javascript
94
+ rect(50, 50, 100, 80, rgba8(0, 255, 0, 255)); // Green outline
95
+ rect(50, 50, 100, 80, rgba8(0, 255, 0, 255), true); // Green filled
96
+ ```
97
+
98
+ #### `rectfill(x, y, width, height, color)`
99
+
100
+ Draw a filled rectangle (alias for `rect(..., true)`).
101
+
102
+ ```javascript
103
+ rectfill(10, 10, 200, 100, rgba8(255, 128, 0, 255)); // Orange rectangle
104
+ ```
105
+
106
+ #### `circle(x, y, radius, color, fill?)`
107
+
108
+ Draw a circle.
109
+
110
+ ```javascript
111
+ circle(320, 180, 50, rgba8(255, 0, 255, 255)); // Magenta outline
112
+ circle(320, 180, 50, rgba8(255, 0, 255, 255), true); // Magenta filled
113
+ ```
114
+
115
+ #### `print(text, x, y, color?, scale?)`
116
+
117
+ Draw text using built-in bitmap font.
118
+
119
+ ```javascript
120
+ print('HELLO WORLD', 10, 10); // White text
121
+ print('SCORE: 1000', 10, 20, rgba8(255, 255, 0, 255)); // Yellow text
122
+ print('BIG', 100, 100, rgba8(255, 255, 255, 255), 2); // 2x scale (future)
123
+ ```
124
+
125
+ **Built-in Font Characters:**
126
+
127
+ ```
128
+ ABCDEFGHIJKLMNOPQRSTUVWXYZ
129
+ abcdefghijklmnopqrstuvwxyz
130
+ 0123456789
131
+ !@#$%^&*()_+-=[]{}|;:'",.<>?/\~`
132
+ ```
133
+
134
+ ### Camera Functions
135
+
136
+ #### `setCamera(x, y)`
137
+
138
+ Set camera offset for scrolling.
139
+
140
+ ```javascript
141
+ setCamera(playerX - 320, playerY - 180); // Center camera on player
142
+ ```
143
+
144
+ #### `getCamera()`
145
+
146
+ Get current camera position.
147
+
148
+ ```javascript
149
+ const cam = getCamera();
150
+ console.log(`Camera at ${cam.x}, ${cam.y}`);
151
+ ```
152
+
153
+ ---
154
+
155
+ ## 🌈 Color System
156
+
157
+ ### `rgba8(r, g, b, a?)`
158
+
159
+ Create a color from 8-bit RGBA values (0-255).
160
+
161
+ ```javascript
162
+ const red = rgba8(255, 0, 0, 255); // Opaque red
163
+ const green = rgba8(0, 255, 0, 255); // Opaque green
164
+ const blue = rgba8(0, 0, 255, 255); // Opaque blue
165
+ const cyan = rgba8(0, 255, 255, 255); // Cyan
166
+ const yellow = rgba8(255, 255, 0, 255); // Yellow
167
+ const white = rgba8(255, 255, 255, 255); // White
168
+ const trans = rgba8(255, 0, 0, 128); // 50% transparent red
169
+ ```
170
+
171
+ ### Common Colors
172
+
173
+ ```javascript
174
+ const COLORS = {
175
+ black: rgba8(0, 0, 0, 255),
176
+ white: rgba8(255, 255, 255, 255),
177
+ red: rgba8(255, 0, 0, 255),
178
+ green: rgba8(0, 255, 0, 255),
179
+ blue: rgba8(0, 0, 255, 255),
180
+ cyan: rgba8(0, 255, 255, 255),
181
+ magenta: rgba8(255, 0, 255, 255),
182
+ yellow: rgba8(255, 255, 0, 255),
183
+ orange: rgba8(255, 128, 0, 255),
184
+ purple: rgba8(128, 0, 255, 255),
185
+ gray: rgba8(128, 128, 128, 255),
186
+ };
187
+ ```
188
+
189
+ ---
190
+
191
+ ## 🎮 Input System
192
+
193
+ ### Keyboard Input
194
+
195
+ #### `btn(i)` - Check if button is held down
196
+
197
+ Maps button numbers to keys:
198
+
199
+ | Button | Keyboard | Gamepad |
200
+ | ------ | ------------- | ----------- |
201
+ | 0 | ← Arrow Left | D-pad Left |
202
+ | 1 | → Arrow Right | D-pad Right |
203
+ | 2 | ↑ Arrow Up | D-pad Up |
204
+ | 3 | ↓ Arrow Down | D-pad Down |
205
+ | 4 | Z | A/Cross |
206
+ | 5 | X | B/Circle |
207
+ | 6 | C | X/Square |
208
+ | 7 | V | Y/Triangle |
209
+ | 8 | A | L Trigger |
210
+ | 9 | S | R Trigger |
211
+ | 12 | Enter | Start |
212
+ | 13 | Space | Select |
213
+
214
+ ```javascript
215
+ if (btn(0)) {
216
+ // Left arrow held
217
+ playerX -= 5;
218
+ }
219
+ if (btn(4)) {
220
+ // Z key held
221
+ shoot();
222
+ }
223
+ ```
224
+
225
+ #### `btnp(i)` - Check if button was just pressed (single frame)
226
+
227
+ ```javascript
228
+ if (btnp(13)) {
229
+ // Space just pressed
230
+ jump();
231
+ }
232
+ ```
233
+
234
+ #### `isKeyDown(keyCode)` - Direct key checking (held)
235
+
236
+ ```javascript
237
+ if (isKeyDown('ArrowLeft')) playerX -= 2;
238
+ if (isKeyDown('ArrowRight')) playerX += 2;
239
+ if (isKeyDown('a') || isKeyDown('A')) shootLeft();
240
+ if (isKeyDown('Space')) boost();
241
+ ```
242
+
243
+ #### `isKeyPressed(keyCode)` - Direct key checking (just pressed)
244
+
245
+ ```javascript
246
+ if (isKeyPressed('Enter')) startGame();
247
+ if (isKeyPressed('Space')) jump();
248
+ if (isKeyPressed('r') || isKeyPressed('R')) restart();
249
+ ```
250
+
251
+ **Key Code Examples:**
252
+
253
+ - Arrow keys: `'ArrowLeft'`, `'ArrowRight'`, `'ArrowUp'`, `'ArrowDown'`
254
+ - Letters: `'a'`, `'A'`, `'KeyA'` (all work)
255
+ - Special: `'Space'`, `'Enter'`, `'Shift'`, `'Escape'`
256
+
257
+ ### Mouse Input
258
+
259
+ #### `mouseX()` / `mouseY()`
260
+
261
+ Get mouse position (scaled to 640×360).
262
+
263
+ ```javascript
264
+ const mx = mouseX();
265
+ const my = mouseY();
266
+ print(`Mouse: ${mx}, ${my}`, 10, 10);
267
+ ```
268
+
269
+ #### `mouseDown()` / `mousePressed()`
270
+
271
+ Check mouse button state.
272
+
273
+ ```javascript
274
+ if (mouseDown()) {
275
+ // Mouse button held
276
+ dragObject(mouseX(), mouseY());
277
+ }
278
+
279
+ if (mousePressed()) {
280
+ // Mouse just clicked
281
+ clickButton(mouseX(), mouseY());
282
+ }
283
+ ```
284
+
285
+ ### Gamepad Input
286
+
287
+ #### `gamepadConnected()`
288
+
289
+ Check if a gamepad is connected.
290
+
291
+ ```javascript
292
+ if (gamepadConnected()) {
293
+ print('🎮 GAMEPAD READY', 10, 10);
294
+ }
295
+ ```
296
+
297
+ #### Analog Stick Functions
298
+
299
+ ```javascript
300
+ // Left stick
301
+ const lx = leftStickX(); // -1.0 to 1.0
302
+ const ly = leftStickY(); // -1.0 to 1.0
303
+
304
+ // Right stick
305
+ const rx = rightStickX(); // -1.0 to 1.0
306
+ const ry = rightStickY(); // -1.0 to 1.0
307
+
308
+ // Deadzone: 0.15 (automatically applied)
309
+ ```
310
+
311
+ **Example: Smooth analog movement**
312
+
313
+ ```javascript
314
+ function update(dt) {
315
+ const moveSpeed = 100; // pixels per second
316
+
317
+ // Analog stick movement
318
+ if (gamepadConnected()) {
319
+ playerX += leftStickX() * moveSpeed * dt;
320
+ playerY += leftStickY() * moveSpeed * dt;
321
+ }
322
+
323
+ // Keyboard fallback
324
+ if (btn(0)) playerX -= moveSpeed * dt; // Left
325
+ if (btn(1)) playerX += moveSpeed * dt; // Right
326
+ if (btn(2)) playerY -= moveSpeed * dt; // Up
327
+ if (btn(3)) playerY += moveSpeed * dt; // Down
328
+ }
329
+ ```
330
+
331
+ ---
332
+
333
+ ## 🎲 3D Graphics API
334
+
335
+ Nova64 uses Three.js for 3D rendering with a simplified API.
336
+
337
+ ### Camera Functions
338
+
339
+ #### `setCameraPosition(x, y, z)`
340
+
341
+ Set camera position in 3D space.
342
+
343
+ ```javascript
344
+ setCameraPosition(0, 10, 20); // Behind and above origin
345
+ ```
346
+
347
+ #### `setCameraTarget(x, y, z)`
348
+
349
+ Set what the camera looks at.
350
+
351
+ ```javascript
352
+ setCameraTarget(0, 0, 0); // Look at origin
353
+ ```
354
+
355
+ #### `setCameraLookAt(x, y, z)`
356
+
357
+ Set camera direction vector (for FPS controls).
358
+
359
+ ```javascript
360
+ setCameraLookAt(player.x, player.y, player.z);
361
+ ```
362
+
363
+ #### `setCameraFOV(degrees)`
364
+
365
+ Set field of view (30-120, default 75).
366
+
367
+ ```javascript
368
+ setCameraFOV(90); // Wider view for FPS games
369
+ ```
370
+
371
+ ### Lighting
372
+
373
+ #### `setAmbientLight(hexColor)`
374
+
375
+ Set global ambient lighting.
376
+
377
+ ```javascript
378
+ setAmbientLight(0x404040); // Gray ambient
379
+ setAmbientLight(0x1a1a2a); // Dark blue ambient
380
+ ```
381
+
382
+ #### `setLightDirection(x, y, z)`
383
+
384
+ Set the main directional light direction vector.
385
+
386
+ ```javascript
387
+ setLightDirection(-0.5, -1, -0.3); // From top-left
388
+ setLightDirection(1, 1, 0.5); // From upper-right
389
+ ```
390
+
391
+ #### `setLightColor(hexColor, intensity?)`
392
+
393
+ Configure directional light colour and intensity.
394
+
395
+ ```javascript
396
+ setLightColor(0xffffff, 1.0); // White light, full intensity
397
+ setLightColor(0xffd4a0, 0.8); // Warm sunset light
398
+ ```
399
+
400
+ #### `createPointLight(hexColor, intensity, [x, y, z], distance?)`
401
+
402
+ Add a point light at a world-space position.
403
+
404
+ ```javascript
405
+ const lamp = createPointLight(0xff8800, 2.0, [5, 3, 0], 20);
406
+ ```
407
+
408
+ ### Fog
409
+
410
+ #### `setFog(hexColor, near, far)`
411
+
412
+ Add distance fog for atmosphere.
413
+
414
+ ```javascript
415
+ setFog(0x000020, 30, 150); // Dark blue fog
416
+ ```
417
+
418
+ ### 3D Objects
419
+
420
+ #### `createCube(size, color, position, options?)`
421
+
422
+ Create a cube mesh.
423
+
424
+ ```javascript
425
+ const cube = createCube(2, 0xff0000, [0, 0, 0]); // Red 2x2x2 cube
426
+ const box = createCube(1, 0x00ff00, [5, 1, 0], {
427
+ flatShading: true,
428
+ });
429
+ ```
430
+
431
+ #### `createAdvancedCube(size, materialOptions, position)`
432
+
433
+ Create cube with emissive materials (for bloom).
434
+
435
+ ```javascript
436
+ const neonCube = createAdvancedCube(
437
+ 1,
438
+ {
439
+ color: 0x00ffff, // Cyan base
440
+ emissive: 0x00ffff, // Cyan glow
441
+ emissiveIntensity: 0.8, // Glow strength (0-2+)
442
+ flatShading: true, // Retro look
443
+ },
444
+ [0, 1, 0]
445
+ );
446
+ ```
447
+
448
+ #### `createSphere(radius, color, position, segments?, options?)`
449
+
450
+ Create a sphere.
451
+
452
+ ```javascript
453
+ const sphere = createSphere(1, 0xff00ff, [0, 2, 0], 8); // Low-poly
454
+ const glowSphere = createSphere(1, 0xffff00, [3, 1, 0], 12, {
455
+ emissive: 0xffff00,
456
+ emissiveIntensity: 1.0,
457
+ });
458
+ ```
459
+
460
+ #### `createPlane(width, depth, color, position)`
461
+
462
+ Create a flat plane.
463
+
464
+ ```javascript
465
+ const floor = createPlane(100, 100, 0x008800, [0, 0, 0]);
466
+ ```
467
+
468
+ ### Mesh Manipulation
469
+
470
+ #### `setPosition(meshId, x, y, z)` or `setPosition(meshId, [x, y, z])`
471
+
472
+ Move a mesh.
473
+
474
+ ```javascript
475
+ setPosition(cube, playerX, playerY, playerZ);
476
+ setPosition(sphere, [10, 5, 3]);
477
+ ```
478
+
479
+ #### `setRotation(meshId, x, y, z)` or `setRotation(meshId, [x, y, z])`
480
+
481
+ Rotate a mesh (radians).
482
+
483
+ ```javascript
484
+ setRotation(cube, 0, Math.PI / 4, 0); // Rotate 45° around Y
485
+ setRotation(box, [angle, 0, 0]);
486
+ ```
487
+
488
+ #### `setScale(meshId, x, y, z)` or `setScale(meshId, [x, y, z])`
489
+
490
+ Scale a mesh.
491
+
492
+ ```javascript
493
+ setScale(cube, 2, 1, 2); // Wide and deep
494
+ setScale(sphere, [0.5, 0.5, 0.5]); // Half size
495
+ ```
496
+
497
+ #### `destroyMesh(meshId)`
498
+
499
+ Remove a mesh from the scene.
500
+
501
+ ```javascript
502
+ destroyMesh(cube);
503
+ ```
504
+
505
+ ### Skybox
506
+
507
+ #### `createSpaceSkybox(options?)`
508
+
509
+ Procedural starfield and nebulae — the default Nova64 space look.
510
+
511
+ ```javascript
512
+ createSpaceSkybox(); // Default stars + nebulae
513
+ createSpaceSkybox({ starCount: 2000, nebulaCount: 4 });
514
+ ```
515
+
516
+ #### `createGradientSkybox(topColor, bottomColor)`
517
+
518
+ Two-colour gradient sky — great for outdoor scenes and sunsets.
519
+
520
+ ```javascript
521
+ createGradientSkybox(0x0077ff, 0x004488); // Blue sky
522
+ createGradientSkybox(0xff6a00, 0x1a0033); // Sunset
523
+ ```
524
+
525
+ #### `createSolidSkybox(color)`
526
+
527
+ Flat solid colour sky — good for caves or indoor scenes.
528
+
529
+ ```javascript
530
+ createSolidSkybox(0x000000); // Pure black
531
+ ```
532
+
533
+ #### `createImageSkybox([px, nx, py, ny, pz, nz])`
534
+
535
+ Cube-map skybox from 6 image URLs. Also enables image-based lighting (IBL).
536
+
537
+ ```javascript
538
+ createImageSkybox([
539
+ '/assets/sky_px.jpg',
540
+ '/assets/sky_nx.jpg',
541
+ '/assets/sky_py.jpg',
542
+ '/assets/sky_ny.jpg',
543
+ '/assets/sky_pz.jpg',
544
+ '/assets/sky_nz.jpg',
545
+ ]);
546
+ ```
547
+
548
+ #### `clearSkybox()`
549
+
550
+ Remove the current skybox.
551
+
552
+ ```javascript
553
+ clearSkybox();
554
+ ```
555
+
556
+ #### `enableSkyboxAutoAnimate(speed?)` / `disableSkyboxAutoAnimate()`
557
+
558
+ Auto-rotate the skybox every frame.
559
+
560
+ ```javascript
561
+ enableSkyboxAutoAnimate(0.5); // Slow drift
562
+ disableSkyboxAutoAnimate();
563
+ ```
564
+
565
+ #### `animateSkybox(dt)`
566
+
567
+ Manually advance skybox animation by delta-time (call in `update`).
568
+
569
+ ```javascript
570
+ export function update(dt) {
571
+ animateSkybox(dt);
572
+ }
573
+ ```
574
+
575
+ #### `setSkyboxSpeed(multiplier)`
576
+
577
+ Scale the auto-animation speed.
578
+
579
+ ```javascript
580
+ setSkyboxSpeed(2.0); // Double speed
581
+ ```
582
+
583
+ ---
584
+
585
+ ## ✨ Visual Effects API
586
+
587
+ ### Post-Processing
588
+
589
+ #### `enableBloom(strength?, radius?, threshold?)`
590
+
591
+ Add bloom glow effect.
592
+
593
+ ```javascript
594
+ enableBloom(); // Default settings
595
+ enableBloom(1.2, 0.6, 0.3); // Balanced neon glow
596
+ enableBloom(2.0, 0.8, 0.2); // Strong dramatic glow
597
+ ```
598
+
599
+ **Parameters:**
600
+
601
+ - `strength` (0.5-3.0): Glow intensity. **Optimal: 1.0-1.5**
602
+ - `radius` (0.0-1.0): Glow spread. **Optimal: 0.6-0.8**
603
+ - `threshold` (0.0-1.0): Brightness cutoff. **Optimal: 0.2-0.4**
604
+
605
+ #### `disableBloom()`
606
+
607
+ Turn off bloom effect.
608
+
609
+ ```javascript
610
+ disableBloom();
611
+ ```
612
+
613
+ #### `setBloomStrength(value)`
614
+
615
+ Adjust bloom strength at runtime.
616
+
617
+ ```javascript
618
+ setBloomStrength(1.5); // Increase intensity
619
+ ```
620
+
621
+ #### `enableFXAA()`
622
+
623
+ Enable anti-aliasing (smooths edges).
624
+
625
+ ```javascript
626
+ enableFXAA(); // Usually paired with bloom
627
+ ```
628
+
629
+ #### `disableFXAA()`
630
+
631
+ Disable anti-aliasing.
632
+
633
+ ```javascript
634
+ disableFXAA();
635
+ ```
636
+
637
+ ### Optimal Bloom Settings Guide
638
+
639
+ | Use Case | Strength | Radius | Threshold | Emissive |
640
+ | ----------- | -------- | ------- | --------- | -------- |
641
+ | Subtle glow | 0.5-0.8 | 0.4-0.5 | 0.4-0.5 | 0.3-0.5 |
642
+ | Neon/Tron | 1.0-1.5 | 0.6-0.8 | 0.2-0.3 | 0.6-0.8 |
643
+ | Dramatic | 1.5-2.0 | 0.8-1.0 | 0.1-0.2 | 0.8-1.2 |
644
+ | Extreme | 2.0+ | 1.0 | 0.1 | 1.0+ |
645
+
646
+ **Warning:** Too much bloom causes white-out! Keep strength ≤ 1.5 for most scenes.
647
+
648
+ ### Example: Neon Scene
649
+
650
+ ```javascript
651
+ export function init() {
652
+ // Enable balanced bloom for neon aesthetic
653
+ enableBloom(1.2, 0.6, 0.3);
654
+ enableFXAA();
655
+
656
+ // Dark environment
657
+ setAmbientLight(0x1a1a2a);
658
+ setFog(0x000020, 30, 150);
659
+
660
+ // Glowing objects
661
+ const neonCube = createAdvancedCube(
662
+ 1,
663
+ {
664
+ color: 0x00ffff,
665
+ emissive: 0x00ffff,
666
+ emissiveIntensity: 0.8, // Sweet spot for visibility
667
+ },
668
+ [0, 1, 0]
669
+ );
670
+ }
671
+ ```
672
+
673
+ ---
674
+
675
+ ## 🧱 Voxel Engine API
676
+
677
+ Build Minecraft-style block worlds.
678
+
679
+ ### World Management
680
+
681
+ #### `updateVoxelWorld(playerX, playerZ)`
682
+
683
+ Load/unload chunks around player.
684
+
685
+ ```javascript
686
+ updateVoxelWorld(player.x, player.z); // Call when player moves
687
+ ```
688
+
689
+ ### Block Types
690
+
691
+ ```javascript
692
+ BLOCK_TYPES = {
693
+ AIR: 0,
694
+ GRASS: 1,
695
+ DIRT: 2,
696
+ STONE: 3,
697
+ SAND: 4,
698
+ WATER: 5, // Transparent
699
+ WOOD: 6,
700
+ LEAVES: 7,
701
+ COBBLESTONE: 8,
702
+ PLANKS: 9,
703
+ GLASS: 10, // Transparent
704
+ BRICK: 11,
705
+ SNOW: 12,
706
+ ICE: 13,
707
+ BEDROCK: 14,
708
+ };
709
+ ```
710
+
711
+ ### Block Interaction
712
+
713
+ #### `getVoxelBlock(x, y, z)`
714
+
715
+ Get block type at position.
716
+
717
+ ```javascript
718
+ const block = getVoxelBlock(10, 35, 10);
719
+ if (block === BLOCK_TYPES.STONE) {
720
+ console.log('Found stone!');
721
+ }
722
+ ```
723
+
724
+ #### `setVoxelBlock(x, y, z, blockType)`
725
+
726
+ Place or remove a block.
727
+
728
+ ```javascript
729
+ setVoxelBlock(10, 35, 10, BLOCK_TYPES.STONE); // Place stone
730
+ setVoxelBlock(10, 35, 10, BLOCK_TYPES.AIR); // Remove block
731
+ ```
732
+
733
+ #### `raycastVoxelBlock(origin, direction, maxDistance)`
734
+
735
+ Find block player is looking at.
736
+
737
+ ```javascript
738
+ const result = raycastVoxelBlock(
739
+ [player.x, player.y + 1.6, player.z], // Eye position
740
+ lookDirection, // [dx, dy, dz]
741
+ 10 // Max reach
742
+ );
743
+
744
+ if (result.hit) {
745
+ const [x, y, z] = result.position;
746
+ console.log(`Looking at block at ${x}, ${y}, ${z}`);
747
+ }
748
+ ```
749
+
750
+ ### Physics
751
+
752
+ #### `checkVoxelCollision(position, size)`
753
+
754
+ Check if position collides with solid blocks.
755
+
756
+ ```javascript
757
+ const colliding = checkVoxelCollision(
758
+ [player.x, player.y, player.z],
759
+ 0.3 // Collision radius
760
+ );
761
+
762
+ if (colliding) {
763
+ console.log('Hit a block!');
764
+ }
765
+ ```
766
+
767
+ ### Structures
768
+
769
+ #### `placeVoxelTree(x, y, z)`
770
+
771
+ Generate a tree.
772
+
773
+ ```javascript
774
+ placeVoxelTree(20, 35, 20); // Trunk + leaves
775
+ ```
776
+
777
+ ### Example: Basic Voxel Game
778
+
779
+ ```javascript
780
+ let player = { x: 0, y: 50, z: 0, vy: 0 };
781
+
782
+ export function init() {
783
+ updateVoxelWorld(0, 0); // Initial world gen
784
+ setCameraFOV(95); // Wide FOV for voxels
785
+ }
786
+
787
+ export function update(dt) {
788
+ // Movement
789
+ const speed = 10;
790
+ if (btn(0)) player.x -= speed * dt;
791
+ if (btn(1)) player.x += speed * dt;
792
+ if (btn(2)) player.z -= speed * dt;
793
+ if (btn(3)) player.z += speed * dt;
794
+
795
+ // Gravity
796
+ player.vy -= 20 * dt;
797
+ player.y += player.vy * dt;
798
+
799
+ // Ground collision
800
+ const onGround = checkVoxelCollision([player.x, player.y, player.z], 0.3);
801
+ if (onGround && player.vy < 0) {
802
+ player.vy = 0;
803
+ }
804
+
805
+ // Jump
806
+ if (btnp(13) && onGround) {
807
+ player.vy = 10;
808
+ }
809
+
810
+ // Update world
811
+ updateVoxelWorld(player.x, player.z);
812
+ }
813
+ ```
814
+
815
+ ---
816
+
817
+ ## 🖼️ UI System
818
+
819
+ ### Buttons
820
+
821
+ #### `createButton(x, y, width, height, text, callback, options?)`
822
+
823
+ Create an interactive button.
824
+
825
+ ```javascript
826
+ const startBtn = createButton(
827
+ 200,
828
+ 150,
829
+ 240,
830
+ 60,
831
+ 'START GAME',
832
+ () => {
833
+ gameState = 'playing';
834
+ },
835
+ {
836
+ normalColor: rgba8(0, 128, 255, 255),
837
+ hoverColor: rgba8(50, 150, 255, 255),
838
+ pressedColor: rgba8(0, 100, 200, 255),
839
+ }
840
+ );
841
+ ```
842
+
843
+ #### `updateAllButtons()`
844
+
845
+ Check for button clicks (call in `update()`).
846
+
847
+ ```javascript
848
+ export function update(dt) {
849
+ const clicked = updateAllButtons();
850
+ if (clicked) {
851
+ console.log('A button was clicked!');
852
+ }
853
+ }
854
+ ```
855
+
856
+ #### `drawAllButtons()`
857
+
858
+ Render all buttons (call in `draw()`).
859
+
860
+ ```javascript
861
+ export function draw() {
862
+ drawAllButtons();
863
+ }
864
+ ```
865
+
866
+ #### `clearButtons()`
867
+
868
+ Remove all buttons.
869
+
870
+ ```javascript
871
+ clearButtons(); // Clear menu when starting game
872
+ ```
873
+
874
+ ### Panels
875
+
876
+ #### `createPanel(x, y, width, height, options?)`
877
+
878
+ Create a styled panel background.
879
+
880
+ ```javascript
881
+ const panel = createPanel(50, 50, 300, 200, {
882
+ bgColor: rgba8(20, 20, 40, 200), // Semi-transparent dark blue
883
+ borderColor: rgba8(0, 255, 255, 255), // Cyan border
884
+ borderWidth: 3,
885
+ shadow: true,
886
+ gradient: true,
887
+ gradientColor: rgba8(40, 40, 80, 200),
888
+ });
889
+ ```
890
+
891
+ #### `drawPanel(panel)`
892
+
893
+ Draw a panel.
894
+
895
+ ```javascript
896
+ drawPanel(panel);
897
+ ```
898
+
899
+ ### Pre-defined UI Colors
900
+
901
+ ```javascript
902
+ const uiColors = {
903
+ primary: rgba8(0, 128, 255, 255), // Blue
904
+ secondary: rgba8(128, 128, 128, 255), // Gray
905
+ success: rgba8(0, 255, 0, 255), // Green
906
+ warning: rgba8(255, 255, 0, 255), // Yellow
907
+ danger: rgba8(255, 0, 0, 255), // Red
908
+ light: rgba8(220, 220, 220, 255), // Light gray
909
+ dark: rgba8(40, 40, 40, 255), // Dark gray
910
+ };
911
+ ```
912
+
913
+ ### Text Helpers
914
+
915
+ #### `setFont(size)`
916
+
917
+ Change font size (future feature).
918
+
919
+ ```javascript
920
+ setFont('normal'); // 8px (default)
921
+ setFont('large'); // 16px
922
+ setFont('huge'); // 24px
923
+ ```
924
+
925
+ #### `setTextAlign(align)`
926
+
927
+ Set text alignment.
928
+
929
+ ```javascript
930
+ setTextAlign('left'); // Default
931
+ setTextAlign('center'); // Centered
932
+ setTextAlign('right'); // Right-aligned
933
+ ```
934
+
935
+ #### `drawText(text, x, y, color, align?)`
936
+
937
+ Draw text with alignment.
938
+
939
+ ```javascript
940
+ drawText('GAME OVER', 320, 180, rgba8(255, 0, 0, 255), 1); // Centered
941
+ ```
942
+
943
+ #### `drawTextShadow(text, x, y, color, shadowColor, shadowOffset, align?)`
944
+
945
+ Draw text with shadow.
946
+
947
+ ```javascript
948
+ drawTextShadow(
949
+ 'TITLE',
950
+ 320,
951
+ 50,
952
+ rgba8(255, 255, 255, 255), // White text
953
+ rgba8(0, 0, 0, 255), // Black shadow
954
+ 4,
955
+ 1 // Offset, alignment
956
+ );
957
+ ```
958
+
959
+ #### `drawTextOutline(text, x, y, color, outlineColor, thickness, align?)`
960
+
961
+ Draw text with outline.
962
+
963
+ ```javascript
964
+ drawTextOutline(
965
+ 'SCORE: 1000',
966
+ 320,
967
+ 20,
968
+ rgba8(255, 255, 0, 255), // Yellow text
969
+ rgba8(0, 0, 0, 255), // Black outline
970
+ 2,
971
+ 1 // Thickness, alignment
972
+ );
973
+ ```
974
+
975
+ ### Gradient Rectangle
976
+
977
+ #### `drawGradientRect(x, y, width, height, colorTop, colorBottom, vertical?)`
978
+
979
+ Draw a gradient-filled rectangle.
980
+
981
+ ```javascript
982
+ // Vertical gradient
983
+ drawGradientRect(
984
+ 0,
985
+ 0,
986
+ 640,
987
+ 360,
988
+ rgba8(0, 50, 100, 255), // Top: dark blue
989
+ rgba8(0, 0, 20, 255), // Bottom: darker
990
+ true
991
+ );
992
+ ```
993
+
994
+ ---
995
+
996
+ ## 🔊 Audio System
997
+
998
+ ### Music
999
+
1000
+ #### `playMusic(trackName, options?)`
1001
+
1002
+ Play background music.
1003
+
1004
+ ```javascript
1005
+ playMusic('theme', { loop: true, volume: 0.7 });
1006
+ ```
1007
+
1008
+ #### `stopMusic()`
1009
+
1010
+ Stop current music.
1011
+
1012
+ ```javascript
1013
+ stopMusic();
1014
+ ```
1015
+
1016
+ ### Sound Effects
1017
+
1018
+ #### `playSound(soundName, options?)`
1019
+
1020
+ Play a sound effect.
1021
+
1022
+ ```javascript
1023
+ playSound('jump', { volume: 0.5 });
1024
+ playSound('explosion', { volume: 1.0, pitch: 1.2 });
1025
+ ```
1026
+
1027
+ #### `stopSound(soundName)`
1028
+
1029
+ Stop a specific sound.
1030
+
1031
+ ```javascript
1032
+ stopSound('laser');
1033
+ ```
1034
+
1035
+ ---
1036
+
1037
+ ## 🛠️ Utility Functions
1038
+
1039
+ ### Math Helpers
1040
+
1041
+ ```javascript
1042
+ // Clamp value between min and max
1043
+ function clamp(value, min, max) {
1044
+ return Math.max(min, Math.min(max, value));
1045
+ }
1046
+
1047
+ // Linear interpolation
1048
+ function lerp(a, b, t) {
1049
+ return a + (b - a) * t;
1050
+ }
1051
+
1052
+ // Distance between two points
1053
+ function dist(x1, y1, x2, y2) {
1054
+ const dx = x2 - x1;
1055
+ const dy = y2 - y1;
1056
+ return Math.sqrt(dx * dx + dy * dy);
1057
+ }
1058
+
1059
+ // Random integer between min and max (inclusive)
1060
+ function rnd(min, max) {
1061
+ return Math.floor(Math.random() * (max - min + 1)) + min;
1062
+ }
1063
+
1064
+ // Random float between min and max
1065
+ function rndFloat(min, max) {
1066
+ return Math.random() * (max - min) + min;
1067
+ }
1068
+ ```
1069
+
1070
+ ### Timing
1071
+
1072
+ #### `getDeltaTime()`
1073
+
1074
+ Get frame delta time (seconds).
1075
+
1076
+ ```javascript
1077
+ const dt = getDeltaTime(); // ~0.016 for 60 FPS
1078
+ ```
1079
+
1080
+ #### `getFPS()`
1081
+
1082
+ Get current frames per second.
1083
+
1084
+ ```javascript
1085
+ const fps = getFPS();
1086
+ print(`FPS: ${Math.round(fps)}`, 10, 10);
1087
+ ```
1088
+
1089
+ ### Storage
1090
+
1091
+ #### `save(key, value)`
1092
+
1093
+ Save data to browser storage.
1094
+
1095
+ ```javascript
1096
+ save('highScore', 10000);
1097
+ save('playerName', 'ACE');
1098
+ ```
1099
+
1100
+ #### `load(key, defaultValue?)`
1101
+
1102
+ Load data from storage.
1103
+
1104
+ ```javascript
1105
+ const score = load('highScore', 0);
1106
+ const name = load('playerName', 'PLAYER');
1107
+ ```
1108
+
1109
+ ---
1110
+
1111
+ ## 📝 Complete Example Game
1112
+
1113
+ ```javascript
1114
+ // Simple platformer demonstrating multiple APIs
1115
+
1116
+ let player = {
1117
+ x: 100,
1118
+ y: 100,
1119
+ vx: 0,
1120
+ vy: 0,
1121
+ width: 16,
1122
+ height: 16,
1123
+ onGround: false,
1124
+ };
1125
+
1126
+ let gameState = 'menu';
1127
+ let score = 0;
1128
+
1129
+ export function init() {
1130
+ console.log('🎮 Platformer initialized!');
1131
+
1132
+ // Create menu button
1133
+ createButton(
1134
+ 200,
1135
+ 200,
1136
+ 240,
1137
+ 60,
1138
+ 'START GAME',
1139
+ () => {
1140
+ gameState = 'playing';
1141
+ },
1142
+ {
1143
+ normalColor: rgba8(0, 128, 255, 255),
1144
+ hoverColor: rgba8(50, 150, 255, 255),
1145
+ }
1146
+ );
1147
+ }
1148
+
1149
+ export function update(dt) {
1150
+ if (gameState === 'menu') {
1151
+ updateAllButtons();
1152
+ return;
1153
+ }
1154
+
1155
+ // Horizontal movement
1156
+ const moveSpeed = 200;
1157
+ if (btn(0))
1158
+ player.vx = -moveSpeed; // Left
1159
+ else if (btn(1))
1160
+ player.vx = moveSpeed; // Right
1161
+ else player.vx *= 0.8; // Friction
1162
+
1163
+ // Jump
1164
+ if (btnp(13) && player.onGround) {
1165
+ player.vy = -400; // Jump velocity
1166
+ }
1167
+
1168
+ // Gravity
1169
+ player.vy += 1000 * dt;
1170
+
1171
+ // Apply velocity
1172
+ player.x += player.vx * dt;
1173
+ player.y += player.vy * dt;
1174
+
1175
+ // Ground collision (simple)
1176
+ if (player.y > 300) {
1177
+ player.y = 300;
1178
+ player.vy = 0;
1179
+ player.onGround = true;
1180
+ } else {
1181
+ player.onGround = false;
1182
+ }
1183
+
1184
+ // Wrap screen
1185
+ if (player.x < 0) player.x = 640;
1186
+ if (player.x > 640) player.x = 0;
1187
+
1188
+ // Camera follows player
1189
+ setCamera(player.x - 320, 0);
1190
+
1191
+ // Score increases over time
1192
+ score += Math.floor(dt * 10);
1193
+ }
1194
+
1195
+ export function draw() {
1196
+ // Clear to sky blue
1197
+ cls(rgba8(100, 150, 255, 255));
1198
+
1199
+ if (gameState === 'menu') {
1200
+ // Menu screen
1201
+ drawTextShadow('PLATFORMER', 320, 100, rgba8(255, 255, 255, 255), rgba8(0, 0, 0, 255), 4, 1);
1202
+ drawAllButtons();
1203
+ return;
1204
+ }
1205
+
1206
+ // Draw ground
1207
+ rectfill(0, 316, 640, 44, rgba8(50, 150, 50, 255));
1208
+
1209
+ // Draw player
1210
+ rectfill(
1211
+ player.x - player.width / 2,
1212
+ player.y - player.height,
1213
+ player.width,
1214
+ player.height,
1215
+ rgba8(255, 0, 0, 255)
1216
+ );
1217
+
1218
+ // Draw HUD (no camera offset)
1219
+ setCamera(0, 0);
1220
+ print(`SCORE: ${score}`, 10, 10, rgba8(255, 255, 255, 255));
1221
+ print(`FPS: ${Math.round(getFPS())}`, 10, 25, rgba8(255, 255, 0, 255));
1222
+ }
1223
+ ```
1224
+
1225
+ ---
1226
+
1227
+ ## 🎯 Best Practices
1228
+
1229
+ ### Performance
1230
+
1231
+ 1. **Use `rectfill()` instead of filled `rect()`** - More explicit
1232
+ 2. **Minimize `pset()` calls** - Draw shapes instead of individual pixels
1233
+ 3. **Clear buttons when not needed** - Call `clearButtons()` on state changes
1234
+ 4. **Use camera for scrolling** - Don't offset every draw call manually
1235
+ 5. **Keep bloom moderate** - Strength 1.0-1.5, emissive 0.6-0.8
1236
+ 6. **Update voxel world sparingly** - Only when player moves significantly
1237
+
1238
+ ### Code Organization
1239
+
1240
+ ```javascript
1241
+ // Good: Separate concerns
1242
+ const GAME_STATES = {
1243
+ MENU: 'menu',
1244
+ PLAYING: 'playing',
1245
+ PAUSED: 'paused',
1246
+ GAMEOVER: 'gameover',
1247
+ };
1248
+
1249
+ let gameState = GAME_STATES.MENU;
1250
+
1251
+ function updateMenu(dt) {
1252
+ /* ... */
1253
+ }
1254
+ function updatePlaying(dt) {
1255
+ /* ... */
1256
+ }
1257
+ function updateGameOver(dt) {
1258
+ /* ... */
1259
+ }
1260
+
1261
+ export function update(dt) {
1262
+ switch (gameState) {
1263
+ case GAME_STATES.MENU:
1264
+ updateMenu(dt);
1265
+ break;
1266
+ case GAME_STATES.PLAYING:
1267
+ updatePlaying(dt);
1268
+ break;
1269
+ case GAME_STATES.GAMEOVER:
1270
+ updateGameOver(dt);
1271
+ break;
1272
+ }
1273
+ }
1274
+ ```
1275
+
1276
+ ### Input Handling
1277
+
1278
+ ```javascript
1279
+ // Good: Support multiple input methods
1280
+ function update(dt) {
1281
+ let moving = false;
1282
+
1283
+ // Keyboard
1284
+ if (btn(0)) {
1285
+ player.x -= speed * dt;
1286
+ moving = true;
1287
+ }
1288
+ if (btn(1)) {
1289
+ player.x += speed * dt;
1290
+ moving = true;
1291
+ }
1292
+
1293
+ // Gamepad analog
1294
+ if (gamepadConnected()) {
1295
+ const stickX = leftStickX();
1296
+ if (Math.abs(stickX) > 0.1) {
1297
+ player.x += stickX * speed * dt;
1298
+ moving = true;
1299
+ }
1300
+ }
1301
+
1302
+ // Apply friction when not moving
1303
+ if (!moving) {
1304
+ player.vx *= 0.9;
1305
+ }
1306
+ }
1307
+ ```
1308
+
1309
+ ---
1310
+
1311
+ ## 🐛 Common Issues
1312
+
1313
+ ### "Function is not defined"
1314
+
1315
+ **Problem:** Calling API function that doesn't exist.
1316
+ **Solution:** Check spelling, ensure function is exposed by runtime.
1317
+
1318
+ ```javascript
1319
+ // Wrong
1320
+ addCube(1, 0xff0000, [0, 0, 0]); // ❌ No such function
1321
+
1322
+ // Right
1323
+ createCube(1, 0xff0000, [0, 0, 0]); // ✅ Correct name
1324
+ ```
1325
+
1326
+ ### White screen / Everything too bright
1327
+
1328
+ **Problem:** Bloom settings too high.
1329
+ **Solution:** Reduce bloom strength and emissive intensities.
1330
+
1331
+ ```javascript
1332
+ // Wrong - causes white-out
1333
+ enableBloom(5.0, 1.0, 0.05); // ❌ Too extreme
1334
+
1335
+ // Right - balanced glow
1336
+ enableBloom(1.2, 0.6, 0.3); // ✅ Visible details
1337
+ ```
1338
+
1339
+ ### 3D scene not visible
1340
+
1341
+ **Problem:** Drawing `cls()` in `draw()` clears GPU-rendered scene.
1342
+ **Solution:** Remove `cls()` from `draw()` - 3D is auto-rendered.
1343
+
1344
+ ```javascript
1345
+ // Wrong
1346
+ export function draw() {
1347
+ cls(); // ❌ Clears 3D scene!
1348
+ print('HUD', 10, 10);
1349
+ }
1350
+
1351
+ // Right
1352
+ export function draw() {
1353
+ // ✅ 3D scene renders first automatically
1354
+ print('HUD', 10, 10); // 2D overlay on top
1355
+ }
1356
+ ```
1357
+
1358
+ ### Button not responding
1359
+
1360
+ **Problem:** Not calling `updateAllButtons()` or button callback not firing.
1361
+ **Solution:** Call update function, check keyboard support with `isKeyDown()`.
1362
+
1363
+ ```javascript
1364
+ export function update(dt) {
1365
+ updateAllButtons(); // ✅ Required!
1366
+
1367
+ // Keyboard fallback
1368
+ if (isKeyDown('Space') || isKeyDown('Enter')) {
1369
+ startGame();
1370
+ }
1371
+ }
1372
+ ```
1373
+
1374
+ ---
1375
+
1376
+ ## 📚 Additional Resources
1377
+
1378
+ - **EFFECTS_API_GUIDE.md** - Detailed effects documentation
1379
+ - **VOXEL_ENGINE_GUIDE.md** - Complete voxel system guide
1380
+ - **START_SCREEN_GUIDE.md** - Creating start screens
1381
+ - **GAMEPAD_SUPPORT.md** - Controller mapping details
1382
+ - **Examples folder** - 20+ working game demos
1383
+
1384
+ ---
1385
+
1386
+ ## 🎨 Example Games Included
1387
+
1388
+ 1. **hello-3d** - Basic 3D shapes
1389
+ 2. **demoscene** - Visual effects showcase (5 scenes)
1390
+ 3. **tron-racer-3d** - Light cycle racing with bloom
1391
+ 4. **cyberpunk-city-3d** - Flying city explorer
1392
+ 5. **minecraft-demo** - Voxel world builder
1393
+ 6. **f-zero-nova-3d** - Futuristic racing
1394
+ 7. **star-fox-nova-3d** - Space combat
1395
+ 8. **shooter-demo-3d** - Space shooter
1396
+ 9. **3d-advanced** - Epic space battle
1397
+ 10. **knight-platformer** - Side-scrolling action
1398
+
1399
+ ---
1400
+
1401
+ **Built with ❤️ by the Nova64 team**
1402
+ **Target Platform:** Nintendo 64 / PlayStation aesthetic
1403
+ **Resolution:** 640×360 @ 60 FPS
1404
+ **Powered by:** Three.js + Custom Runtime
1405
+
1406
+ 🎮 **Happy Game Development!** 🎮