littlejsengine 1.18.0 → 1.18.1

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.
package/CLAUDE.md ADDED
@@ -0,0 +1 @@
1
+ See @AI.md for instructions.
package/README.md CHANGED
@@ -18,6 +18,10 @@ LittleJS is a fast, lightweight, and fully open source HTML5 game engine designe
18
18
  Its small footprint is packed with a comprehensive feature set including rendering, physics, particles, sound, and input handling.
19
19
  The code is very clean and well documented with many examples to get you started quickly.
20
20
 
21
+ ### 🚀 Join the LittleJS Game Jam
22
+
23
+ *The Third Annual LittleJS Game Jam will take place From Oct 2 to Nov 2! Unleash your creativity and develop amazing games using the LittleJS game engine. đŸ•šī¸đŸŽŽ [Sign up today and get more info about the jam on itch.io!](https://itch.io/jam/littlejs-jam)*
24
+
21
25
  <div align='center'>
22
26
 
23
27
  ## [Demos](https://killedbyapixel.github.io/LittleJS/examples) | [Docs](https://killedbyapixel.github.io/LittleJS/docs) | [Trailer](https://youtu.be/chuBzGjv7Ms) | [Discord](https://discord.gg/zb7hcGkyZe) | [FAQ](https://github.com/KilledByAPixel/LittleJS/blob/main/FAQ.md)
@@ -30,14 +34,15 @@ The code is very clean and well documented with many examples to get you started
30
34
 
31
35
  ### ✨ Graphics
32
36
 
33
- - Super fast WebGL2 + Canvas2D hybrid rendering system
37
+ - Blazing fast WebGL2 + Canvas2D hybrid rendering system
34
38
  - Apply [Shadertoy](https://www.shadertoy.com) style shaders for post-processing effects
35
39
  - Robust particle effect system and [effect design tool](https://killedbyapixel.github.io/LittleJS/examples/particles/)
36
40
 
37
41
  ### 🔊 Audio
38
42
 
39
43
  - Sound and music with mp3, ogg, or wave files
40
- - Use [ZzFX](https://killedbyapixel.github.io/ZzFX/) sound effect generator to play sounds without asset files
44
+ - Use [ZzFX](https://killedbyapixel.github.io/ZzFX/) sound generator to play sounds without asset files
45
+ - Spatial audio stereo panning
41
46
 
42
47
  ### 🎮 Input
43
48
 
@@ -53,13 +58,14 @@ The code is very clean and well documented with many examples to get you started
53
58
  ### 🚀 Flexibility
54
59
 
55
60
  - Compatible with all modern web browsers and mobile devices
56
- - TypeScript and Module support with example projects for both
61
+ - Full TypeScript and Module support with example projects for both
57
62
  - Great for size coding competitions like [Js13kGames](https://js13kgames.com/)
58
63
  - Open Source and [MIT licensed](https://github.com/KilledByAPixel/LittleJS/blob/main/LICENSE)
59
64
 
60
65
  ### đŸ› ī¸ Developer Tools
61
66
 
62
67
  - Live example browser with code editor
68
+ - Import level editor data from [Tiled](https://github.com/mapeditor/tiled) or other JSON
63
69
  - Debug overlay and primitive rendering system
64
70
  - Medal tracking system with [Newgrounds](https://www.newgrounds.com/) support
65
71
  - Node.js build system
@@ -74,6 +80,8 @@ To get started download the latest LittleJS package from GitHub or install via n
74
80
  - [LittleJS Quick Reference Sheet](https://github.com/KilledByAPixel/LittleJS/blob/main/reference.md) - A reference sheet to help you get started.
75
81
  - [Little JS FAQ](https://github.com/KilledByAPixel/LittleJS/blob/main/FAQ.md) - Answers to common questions about LittleJS.
76
82
  - [JS13k Branch](https://github.com/KilledByAPixel/LittleJS/tree/js13k) - For size coding events like JS13k there is a special branch that builds to a 7KB zip.
83
+ - [LittleJS GPT AI](https://chatgpt.com/g/g-67c7c080b5bc81919736bc8815836be6-make-games-with-littlejs) - Use AI to make games without writing any code!
84
+ - [LittleJS AI Info](https://github.com/KilledByAPixel/LittleJS-AI) - Experiments, games, and templates to improve LittleJS + AI workflows.
77
85
 
78
86
  ## Examples
79
87
 
@@ -82,7 +90,7 @@ LittleJS comes with a several demos both for learning and using as starter proje
82
90
  - [Example Browser](https://killedbyapixel.github.io/LittleJS/examples/) - Live example browser with all examples
83
91
  - [Breakout](https://killedbyapixel.github.io/LittleJS/examples/breakout/) - Block breaking game with post-processing effects
84
92
  - [Puzzle Game](https://killedbyapixel.github.io/LittleJS/examples/puzzle/) - Match 3 puzzle game with HD rendering and high score tracking
85
- - [Platformer](https://killedbyapixel.github.io/LittleJS/examples/platformer/) - Platformer/shooter that loads level data from [Tiled Editor](https://github.com/mapeditor/tiled)
93
+ - [Platformer](https://killedbyapixel.github.io/LittleJS/examples/platformer/) - Platformer/shooter demo that loads level data
86
94
  - [Box2D Demo](https://killedbyapixel.github.io/LittleJS/examples/box2d/) - Box2D plugin demonstration and testbed
87
95
  - [Stress Test](https://killedbyapixel.github.io/LittleJS/examples/stress/) - Sprite rendering benchmark and music system demo
88
96
  - [Particle System Designer](https://killedbyapixel.github.io/LittleJS/examples/particles/) - Particle system editor and visualizer
@@ -117,6 +125,7 @@ Here are a few of the many amazing games created with LittleJS...
117
125
  - [Bug&Bee](https://itch.io/jam/littlejs-game-jam-2025/results) - Low fi shoot em up with co-op gameplay. LittleJS Jam 1st place! by [eoinmcg](https://eoinmcg.itch.io/)
118
126
  - [Little Platformer](https://psemo.itch.io/little-platformer) - Platformer with many mechanics. LittleJS Jam 2st place! by [PSEMO](https://psemo.itch.io/), [Solita666](https://itch.io/profile/solita666), [GabrielRG](https://gabrielrg.itch.io/), [Nate](https://natesassoon.itch.io/)
119
127
  - [Rogue Pong](https://itch.io/jam/littlejs-game-jam-2025/rate/4004165) - Roguelike crossed with classic pong gameplay. LittleJS Jam 3rd place! by [webdevbrian](https://webdevbrian.itch.io/)
128
+ - [Orbitswarm](https://focaccai.itch.io/orbitswarm) - Fast paced space strategy game. by [Focaccai](https://www.focaccai.com/)
120
129
  - [LittleJS Jam 2024 Results](https://itch.io/jam/littlejs-jam-2024/results) - All the games from the first LittleJS Game Jam.
121
130
  - [LittleJS Jam 2025 Results](https://itch.io/jam/littlejs-game-jam-2025/results) - All the games from the second LittleJS Game Jam.
122
131
 
@@ -377,6 +377,12 @@ declare module "littlejsengine" {
377
377
  * @default
378
378
  * @memberof Settings */
379
379
  export let tilesPixelated: boolean;
380
+ /** Scale factor applied to the canvas backing store for native-resolution rendering.
381
+ * Pass 1 for no scaling, a number for an explicit ratio, or undefined to track devicePixelRatio each frame.
382
+ * @type {number|undefined}
383
+ * @default
384
+ * @memberof Settings */
385
+ export let canvasPixelRatio: number | undefined;
380
386
  /** Default font used for text rendering
381
387
  * @type {string}
382
388
  * @default
@@ -592,6 +598,11 @@ declare module "littlejsengine" {
592
598
  * @param {boolean} pixelated
593
599
  * @memberof Settings */
594
600
  export function setTilesPixelated(pixelated: boolean): void;
601
+ /** Set the canvas pixel ratio.
602
+ * Pass a number for an explicit ratio, or call with no argument to track devicePixelRatio each frame.
603
+ * @param {number} [pixelRatio]
604
+ * @memberof Settings */
605
+ export function setCanvasPixelRatio(pixelRatio?: number): void;
595
606
  /** Set default font used for text rendering
596
607
  * @param {string} font
597
608
  * @memberof Settings */
@@ -35,7 +35,7 @@ const engineName = 'LittleJS';
35
35
  * @type {string}
36
36
  * @default
37
37
  * @memberof Engine */
38
- const engineVersion = '1.18.0';
38
+ const engineVersion = '1.18.1';
39
39
 
40
40
  /** Frames per second to update
41
41
  * @type {number}
@@ -318,12 +318,17 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
318
318
  }
319
319
  else
320
320
  {
321
+ // apply device pixel ratio for crisp rendering
322
+ const dpr = canvasPixelRatio ?? (devicePixelRatio || 1);
323
+ const viewWidth = innerWidth * dpr | 0;
324
+ const viewHeight = innerHeight * dpr | 0;
325
+
321
326
  // get main canvas size based on window size
322
- mainCanvasSize.x = min(innerWidth, canvasMaxSize.x);
323
- mainCanvasSize.y = min(innerHeight, canvasMaxSize.y);
327
+ mainCanvasSize.x = min(viewWidth, canvasMaxSize.x);
328
+ mainCanvasSize.y = min(viewHeight, canvasMaxSize.y);
324
329
 
325
330
  // responsive aspect ratio with native resolution
326
- const innerAspect = innerWidth / innerHeight;
331
+ const innerAspect = viewWidth / viewHeight;
327
332
  ASSERT(canvasMinAspect <= canvasMaxAspect);
328
333
  if (canvasMaxAspect && innerAspect > canvasMaxAspect)
329
334
  {
@@ -337,6 +342,17 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
337
342
  const h = mainCanvasSize.x / canvasMinAspect | 0;
338
343
  mainCanvasSize.y = min(h, canvasMaxSize.y);
339
344
  }
345
+
346
+ // set CSS display size so backing store renders at viewport size
347
+ const cssW = (mainCanvasSize.x / dpr | 0) + 'px';
348
+ const cssH = (mainCanvasSize.y / dpr | 0) + 'px';
349
+ mainCanvas.style.width = cssW;
350
+ mainCanvas.style.height = cssH;
351
+ if (glCanvas)
352
+ {
353
+ glCanvas.style.width = cssW;
354
+ glCanvas.style.height = cssH;
355
+ }
340
356
  }
341
357
 
342
358
  // clear main canvas and set size
@@ -556,8 +572,9 @@ function drawEngineLogo(t)
556
572
 
557
573
  // LittleJS Logo and Splash Screen
558
574
  const x = mainContext;
559
- const w = mainCanvas.width = innerWidth;
560
- const h = mainCanvas.height = innerHeight;
575
+ const dpr = canvasPixelRatio ?? (devicePixelRatio || 1);
576
+ const w = mainCanvas.width = innerWidth * dpr;
577
+ const h = mainCanvas.height = innerHeight * dpr;
561
578
  {
562
579
  // background
563
580
  const p3 = percent(t, 1, .8);
@@ -951,6 +968,8 @@ function debugShowErrors()
951
968
 
952
969
  function debugInit()
953
970
  {
971
+ if (showEngineVersion)
972
+ console.warn("LittleJS DEBUG build loaded. Use the release build for production.");
954
973
  }
955
974
 
956
975
  function debugUpdate()
@@ -1732,7 +1751,7 @@ function lineTest(posStart, posEnd, testFunction, normal)
1732
1751
  // get ray direction and length
1733
1752
  const dx = posEnd.x - posStart.x;
1734
1753
  const dy = posEnd.y - posStart.y;
1735
- const totalLength = hypot(dx, dy);
1754
+ const totalLength = (dx*dx + dy*dy)**.5;
1736
1755
  if (!totalLength) return;
1737
1756
 
1738
1757
  // current integer cell we are in
@@ -2836,6 +2855,13 @@ let canvasPixelated = false;
2836
2855
  * @memberof Settings */
2837
2856
  let tilesPixelated = true;
2838
2857
 
2858
+ /** Scale factor applied to the canvas backing store for native-resolution rendering.
2859
+ * Pass 1 for no scaling, a number for an explicit ratio, or undefined to track devicePixelRatio each frame.
2860
+ * @type {number|undefined}
2861
+ * @default
2862
+ * @memberof Settings */
2863
+ let canvasPixelRatio = 1;
2864
+
2839
2865
  /** Default font used for text rendering
2840
2866
  * @type {string}
2841
2867
  * @default
@@ -3148,6 +3174,12 @@ function setCanvasPixelated(pixelated)
3148
3174
  * @memberof Settings */
3149
3175
  function setTilesPixelated(pixelated) { tilesPixelated = pixelated; }
3150
3176
 
3177
+ /** Set the canvas pixel ratio.
3178
+ * Pass a number for an explicit ratio, or call with no argument to track devicePixelRatio each frame.
3179
+ * @param {number} [pixelRatio]
3180
+ * @memberof Settings */
3181
+ function setCanvasPixelRatio(pixelRatio) { canvasPixelRatio = pixelRatio; }
3182
+
3151
3183
  /** Set default font used for text rendering
3152
3184
  * @param {string} font
3153
3185
  * @memberof Settings */
@@ -3570,10 +3602,10 @@ class EngineObject
3570
3602
  // if already was touching, try to push away
3571
3603
  const deltaPos = oldPos.subtract(o.pos);
3572
3604
  const length = deltaPos.length();
3573
- const pushAwayAccel = .001; // push away if already overlapping
3574
- const velocity = length < .01 ? randVec2(pushAwayAccel) : deltaPos.scale(pushAwayAccel/length);
3605
+ const pushAwayAccel = .001;
3606
+ const velocity = length < .001 ? vec2(0,1) : deltaPos.scale(pushAwayAccel/length);
3575
3607
  this.velocity = this.velocity.add(velocity);
3576
- if (o.mass) // push away if not fixed
3608
+ if (o.mass) // push away other object if not fixed
3577
3609
  o.velocity = o.velocity.subtract(velocity);
3578
3610
 
3579
3611
  debugPhysics && debugOverlap(this.pos, this.size, o.pos, o.size, '#f00');
@@ -4002,6 +4034,7 @@ function tile(index=new Vector2, size=tileDefaultSize, texture=0, padding=tileDe
4002
4034
  const textureInfo = typeof texture === 'number' ?
4003
4035
  textureInfos[texture] : texture;
4004
4036
  ASSERT(textureInfo instanceof TextureInfo, 'tile texture is not loaded');
4037
+ ASSERT(textureInfo.size.x > 0, 'tile texture is not loaded');
4005
4038
 
4006
4039
  // get the position of the tile
4007
4040
  const sizePaddedX = size.x + padding*2;
@@ -5364,7 +5397,7 @@ function inputInit()
5364
5397
  document.addEventListener('mouseup', onMouseUp);
5365
5398
  document.addEventListener('mousemove', onMouseMove);
5366
5399
  document.addEventListener('mouseleave', onMouseLeave);
5367
- document.addEventListener('wheel', onMouseWheel);
5400
+ document.addEventListener('wheel', onMouseWheel, { passive: false });
5368
5401
  document.addEventListener('contextmenu', onContextMenu);
5369
5402
  document.addEventListener('blur', onBlur);
5370
5403
 
@@ -5383,7 +5416,7 @@ function inputInit()
5383
5416
  }
5384
5417
 
5385
5418
  // try to prevent default browser handling of input
5386
- if (!inputPreventDefault || !document.hasFocus() || !e.cancelable) return;
5419
+ if (!inputPreventDefault || !e.cancelable || !document.hasFocus()) return;
5387
5420
 
5388
5421
  // don't break browser shortcuts
5389
5422
  if (e.ctrlKey || e.metaKey || e.altKey) return;
@@ -5442,7 +5475,7 @@ function inputInit()
5442
5475
  mousePosScreen = mouseEventToScreen(vec2(e.x,e.y));
5443
5476
  mouseDeltaScreen = mouseDeltaScreen.add(mousePosScreen.subtract(mousePosScreenLast));
5444
5477
 
5445
- if (inputPreventDefault && document.hasFocus() && e.cancelable)
5478
+ if (inputPreventDefault && e.cancelable && document.hasFocus())
5446
5479
  e.preventDefault();
5447
5480
  }
5448
5481
  function onMouseUp(e)
@@ -5464,7 +5497,12 @@ function inputInit()
5464
5497
  mouseDeltaScreen = mouseDeltaScreen.add(movement);
5465
5498
  }
5466
5499
  function onMouseLeave() { mouseInWindow = false; } // mouse moved off window
5467
- function onMouseWheel(e) { mouseWheel = e.ctrlKey ? 0 : sign(e.deltaY); }
5500
+ function onMouseWheel(e)
5501
+ {
5502
+ mouseWheel = e.ctrlKey ? 0 : sign(e.deltaY);
5503
+ if (inputPreventDefault && e.cancelable && document.hasFocus())
5504
+ e.preventDefault(); // prevent page scrolling
5505
+ }
5468
5506
  function onContextMenu(e) { e.preventDefault(); } // prevent right click menu
5469
5507
  function onBlur() { inputClear(); } // reset input when focus is lost
5470
5508
 
@@ -5514,7 +5552,7 @@ function inputInit()
5514
5552
  wasTouching = touching;
5515
5553
 
5516
5554
  // prevent default handling like copy, magnifier lens, and scrolling
5517
- if (inputPreventDefault && document.hasFocus() && e.cancelable)
5555
+ if (inputPreventDefault && e.cancelable && document.hasFocus())
5518
5556
  e.preventDefault();
5519
5557
 
5520
5558
  // must return true so the document will get focus
@@ -5672,7 +5710,13 @@ function inputUpdate()
5672
5710
  }
5673
5711
 
5674
5712
  // return if gamepads are disabled or not supported
5675
- if (!gamepadsEnable || !navigator || !navigator.getGamepads) return;
5713
+ try {
5714
+ // protect against getGamepads disallowed security error
5715
+ if (!gamepadsEnable || !navigator?.getGamepads)
5716
+ return;
5717
+ } catch(e) {
5718
+ return;
5719
+ }
5676
5720
 
5677
5721
  // only poll gamepads when focused or in debug mode
5678
5722
  if (!debug && !document.hasFocus()) return;
@@ -13060,6 +13104,7 @@ export
13060
13104
  canvasFixedSize,
13061
13105
  canvasPixelated,
13062
13106
  tilesPixelated,
13107
+ canvasPixelRatio,
13063
13108
  fontDefault,
13064
13109
  showSplashScreen,
13065
13110
  headlessMode,
@@ -13106,6 +13151,7 @@ export
13106
13151
  setCanvasFixedSize,
13107
13152
  setCanvasPixelated,
13108
13153
  setTilesPixelated,
13154
+ setCanvasPixelRatio,
13109
13155
  setFontDefault,
13110
13156
  setShowSplashScreen,
13111
13157
  setHeadlessMode,