littlejsengine 1.24.3 → 1.26.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.
- package/CHANGELOG.md +123 -0
- package/EDITOR.md +2 -2
- package/FAQ.md +55 -11
- package/README.md +6 -2
- package/REFERENCE.md +203 -57
- package/dist/littlejs.d.ts +933 -313
- package/dist/littlejs.esm.js +5143 -2419
- package/dist/littlejs.esm.min.js +38 -24
- package/dist/littlejs.js +5132 -2416
- package/dist/littlejs.min.js +37 -23
- package/dist/littlejs.release.js +4905 -2232
- package/package.json +4 -4
- package/plugins/box2d.js +141 -56
- package/plugins/drawUtilities.js +374 -380
- package/plugins/gltf.js +383 -60
- package/plugins/lightSystem.js +412 -30
- package/plugins/math3d.js +1015 -1014
- package/plugins/medalSystem.js +19 -7
- package/plugins/newgrounds.js +5 -6
- package/plugins/parallax.js +1 -0
- package/plugins/particleEffects.js +47 -14
- package/plugins/pathFinder.js +60 -38
- package/plugins/pluginExport.js +11 -0
- package/plugins/postProcess.js +200 -67
- package/plugins/render3d.js +493 -2391
- package/plugins/render3dDebug.js +5 -8
- package/plugins/render3dEditor.js +84 -9
- package/plugins/render3dExtras.js +122 -33
- package/plugins/render3dLevel.js +67 -8
- package/plugins/render3dMesh.js +1158 -0
- package/plugins/render3dObject.js +1451 -0
- package/plugins/render3dVoxels.js +15 -3
- package/plugins/textureSheet.js +9 -8
- package/plugins/threejs.js +1 -1
- package/plugins/tweenSystem.js +117 -87
- package/plugins/uiSystem.js +62 -28
- package/plugins/wavedash.js +229 -0
- package/src/engine.js +192 -40
- package/src/engineAudio.js +47 -27
- package/src/engineBuild.mjs +6 -1
- package/src/engineDebug.js +23 -162
- package/src/engineDraw.js +148 -43
- package/src/engineEditor.js +153 -40
- package/src/engineExport.js +0 -3
- package/src/engineInput.js +31 -25
- package/src/engineMath.js +38 -20
- package/src/engineObject.js +68 -50
- package/src/engineParticles.js +66 -18
- package/src/engineRelease.js +18 -15
- package/src/engineSettings.js +48 -9
- package/src/engineTileLayer.js +44 -18
- package/src/engineUtilities.js +56 -11
- package/src/engineWebGL.js +68 -22
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,129 @@ What changed in each release, newest first. Releases before 1.20.0 are on the
|
|
|
7
7
|
|
|
8
8
|
Nothing yet.
|
|
9
9
|
|
|
10
|
+
## 1.26.0 - 2026-10-07
|
|
11
|
+
|
|
12
|
+
### New Changes
|
|
13
|
+
|
|
14
|
+
- The package exports its plugins and engine source, littlejsengine/plugins/* and littlejsengine/src/*, which it shipped but would not let a tool reach; they are plain scripts, for a script tag or a copy, while the ESM build already holds every plugin
|
|
15
|
+
- DirectionalLight, a sun for the 2D light system: it lights the whole scene from one side, its sunDirection toward the sun as in 3D, foreground casters throw long shadows that fade by shadowLength, and a background marked castBackgroundShadow is lit only at the edges that face it, Frank Engine's look; one per scene, nothing changes for a game without one
|
|
16
|
+
- Wavedash plugin: achievements and leaderboards on Wavedash, the same shape as the Newgrounds plugin. A `WavedashMedal` is a medal with the identifier of its Wavedash achievement, sent until Wavedash takes it and shown with Wavedash's own toast there; `new WavedashPlugin(leaderboards)` calls `Wavedash.init` and makes the game's leaderboards with how each sorts and shows; `wavedash.postScore` and `getScores`, which give up after 15 seconds. An achievement Wavedash refuses for a minute warns once with its identifier. Off Wavedash every call does nothing
|
|
17
|
+
- Common mistakes say so: drawTile given a color where its tile goes asserts, engineInit takes one image file given as a string, a save that can not be read or written warns in release builds too, and the color constants like RED are frozen in release builds too, where changing one changed it everywhere
|
|
18
|
+
- Clearer errors for common mistakes in debug builds: keyIsDown with a number past the mouse buttons, mouseIsDown with a name, a vec2 of strings or a number where a vec2 goes, setGravity, setCameraPos, setCanvasFixedSize, setCanvasMaxSize, setTileDefaultSize and setCanvasClearColor given a number, a Timer of a string, setHex of the wrong length, drawText with a vec2 size, ParticleEmitter arguments out of order, and changing a color constant each say what to pass; rgb and hsl warn once when given values out of 255 or percents
|
|
19
|
+
- A failed assert's error says what went wrong, its message and values, where it said only 'Assert failed!' and left the message in the console, so the example browser's error box, a game's error overlay and a test now show it
|
|
20
|
+
- 3D environment maps: `render3D.environment` is a cube map that reflective surfaces reflect, sharp at a high shininess and blurred at a low one, and `render3D.skyBox` draws one as the sky; `makeCubeMap` paints one in code from a color for each direction and `loadCubeMap` loads six images. A cube map made with no faces captures the scene around a point (`capture`), the way three.js's CubeCamera does, and an object's `environment` reflects one of its own. A 3D level's scene block may name a sky box and an environment as six image urls each, which the 3D editor's Scene box edits. An object's `roughness`, 0 a mirror to 1 matte, sets its shininess the way glTF and three.js measure it, and a glTF material's roughnessFactor is read into it, so a loaded model's highlights and reflections are as sharp as its file says; a part whose material has none is matte, its shininess 1 where it was 16, which shows only with specular or reflectivity set. The 3D Reflections short shows a chrome sphere reflecting the scene around it under a sky painted in code
|
|
21
|
+
- A skinned glTF model bends about a fifth faster: the skin writes the GPU data as it bends, so the upload sends it without packing it again, and its joints' matrices are worked out with no garbage
|
|
22
|
+
- The level editors autosave a big map or 3D level once the edits stop for a moment, where every stroke on a 512 by 512 map wrote megabytes; a small one is saved after every stroke as before, and anything waiting is written when the editor closes, the page hides, the map is saved or another one loads
|
|
23
|
+
- The FAQ and REFERENCE say how far a world can go and how long a game can run before 32-bit floats on the GPU run out of precision
|
|
24
|
+
- The TypeScript example is typed by the shipped littlejs.d.ts in strict mode and imports 'littlejsengine' as an npm game does, an import map giving the browser the build; the Vite starter installs 1.25, where its lock held 1.18.19
|
|
25
|
+
- A Shader's or a post process's code comes after the engine's own in the shader, so a define in it can not rewrite the engine's names, where a #define of one, the vertex color say, changed what was drawn; the 3D shader drops see through texels at the end, after the normal map and texture reads that need the pixels around them
|
|
26
|
+
- Examples: the raycaster walks on a phone, shorts sized for the example browser fill any window, the starter, module and TypeScript examples match again, 3D shorts share one mesh among objects of the same shape, as the instancing one teaches, and smaller fixes
|
|
27
|
+
- Object collision with many solids is far faster: a mover checks only the solids near it, found through a grid, where it checked every one; 2000 movers went from about 1.3 s a frame to 12 ms in a test, and every contact resolves exactly as before, in the same order, but for a solid moved during physics by something other than its own update (a collision callback moving a third object, a lift's updatePhysics moving a crate), which is found where it went from the next update on
|
|
28
|
+
- Input: a key, button or tap released and pressed again in one frame reads as both, where the release was lost, and a keydown the browser does not mark as a repeat for a key already held does not press it again, but with Cmd held it does, since a Mac sends no keyup for a key let go under Cmd
|
|
29
|
+
- Canvas2D draws a texture smooth or pixelated as its own setting says, as WebGL does; the main canvas starts each frame with no alpha, filter or shadow a game left on it; toggleFullscreen refused by the browser leaves no error in the console
|
|
30
|
+
- A tile layer's shadow is cast by its solid cells only, not by negative markers; a color with a NaN in it is black as text in a release build too; glDeleteTexture leaves the engine holding nothing of the texture
|
|
31
|
+
- A tween whose callback throws no longer stops the other tweens moving that update, its error coming out after them
|
|
32
|
+
- glTF: parseGLTF takes the bytes as a typed array or a Node Buffer too, and an accessor off its values' alignment, or a sparse one past its buffer, says so by name where it threw a bare RangeError
|
|
33
|
+
- Matrix4.getRotation is exact just short of straight up or down, where it was off by up to 5e-4; getNearestClearNode searches each ring alone, the same node found
|
|
34
|
+
- A tinted drawTextureWrapped on Canvas2D bakes its tint once and keeps it for an image, where it baked the whole image on every draw (a canvas, which a game may draw into, is still baked at every draw), and draws smooth or pixelated as its texture says
|
|
35
|
+
- Level editor: a Save whose file was written is not taken for a failed one when what follows throws, which downloaded a copy; layers a game makes in code again and again no longer pile up in it; a map shrunk from the top drops a point or rectangle on its top edge
|
|
36
|
+
- Docs: align2D with any canvas size, an exported prefab names the prefabs inside it, a large time scale costs as many updates, the engineStep example, and why a Box2D world made again does not step as a fresh one
|
|
37
|
+
- A medal unlocked before medalsInit stays unlocked and is saved when it is called, where the save it loaded took it back; a later medalsInit with another save name starts from that save
|
|
38
|
+
|
|
39
|
+
### Breaking Changes
|
|
40
|
+
|
|
41
|
+
- The built in video capture is removed: key 8 on the debug overlay, `debugVideoCaptureStart`, `debugVideoCaptureStop` and `debugVideoCaptureIsActive`. A screen recorder does it better, and the FAQ shows how to save a game frame by frame at an exact 60 fps with `setEngineManualStep` and `engineStep`
|
|
42
|
+
- Node 22.12 or later, the version CI runs: package.json's engines asks for it, so installing littlejsengine on Node 20 warns, or fails with engine-strict; a game in the browser needs no Node at all
|
|
43
|
+
- The Electron example is removed, with Electron and electron-packager from the dev dependencies: a game for the desktop sets up Electron or another wrapper itself, loading its index.html as any page
|
|
44
|
+
- render3d.js is in three files: `render3d.js`, the renderer, then `render3dMesh.js`, meshes and the basic builders, and `render3dObject.js`, EngineObject3D, instancing and lights; nothing changes for a game on a build in `dist`, one that loads the plugins one by one adds the two after `render3d.js`
|
|
45
|
+
|
|
46
|
+
### Fixes
|
|
47
|
+
|
|
48
|
+
- A ParallaxLayer casts no shadow in the light system, as a backdrop threw huge ones, a sun's above all; set its castShadow to true for one that should
|
|
49
|
+
- A tween whose callback throws is ended, at any call: made, mid-run, restarted or starting a loop's next iteration, where it was called and threw at every update
|
|
50
|
+
- A texture sheet load that throws, as an atlas whose data can not be read, is forgotten and loaded again on the next call, as a failed file already was; loadSprite and loadTiles too
|
|
51
|
+
- A text field's Backspace takes off a whole character as a reader counts it, an emoji family too, and maxLength counts those; an image font draws a letter with an accent of its own, as é, as its letter
|
|
52
|
+
- An EngineObject3D on a height map is kept above a ridge it goes over in one move, where only the ends of the move were checked; a short move starting on a voxel block's face hits it there
|
|
53
|
+
- particleEffect keeps a negative speed and angle speed, and particleEffectFromEmitter of a finished emitter gives the time it emitted for, where it gave 0, for ever
|
|
54
|
+
- loadSound and a glTF's buffers and images name the file they could not load, with the hint for a page opened from disk; writeSaveData says when the data can not be written as JSON rather than blaming storage
|
|
55
|
+
- A lens flare made while the game is paused shows, as the 3D editor's Scene box makes one, where it waited for an update that never came, and follows the camera while the game stays paused
|
|
56
|
+
- A release build goes on past an error in its first frame, as it does in any later one, where it stopped the game
|
|
57
|
+
- The 3D settle of a shoved object sees a wall that blocks solids without being solid itself; a see-through part of an additive owner passes the fade assert
|
|
58
|
+
- Examples: the platformer's bullets spark where they hit the wall; the Light Shadows short has a sun
|
|
59
|
+
- setEnablePhysicsSolver(false) stops 3D collision too, solids, the level, the speed cap and 3D particles hitting the level, as it does in 2D, where 3D objects still collided
|
|
60
|
+
- Shader.dispose() frees a shader's programs and takes it off the engine's list, which kept every Shader made for the life of the page; a scene that makes its shaders each time it starts can let them go
|
|
61
|
+
- Wavedash: a board or achievement named like a built-in property, as constructor or toString, is one like any other
|
|
62
|
+
- A Box2dObject's EngineObject physics fields, which Box2D does not read (velocity, angleVelocity, damping, angleDamping, mass, friction, restitution, gravityScale), are documented as unused with the Box2D call to use, and a debug build warns once when one is set; damping out of 0 to 1 asserts in setObjectDefaultDamping and setObjectDefaultAngleDamping, and on a 3D object as on a 2D one, so a debug build that set one out of range stops there now
|
|
63
|
+
- A release build's second engineInit prints no second version line and hands back the first call's promise, done when the game is set up, where it was done at once
|
|
64
|
+
- The script build no longer has a top level colorHex, so a game's own colorHex is not a redeclaration
|
|
65
|
+
- Medals: a tab of the game opened before another tab unlocked a medal keeps that unlock when it saves its own, where it wrote the medal locked again; medalsReset still clears them all
|
|
66
|
+
- A 3D object a collision callback sends across a height map's ridge stays where it was sent, and the object pushing it is not carried back, where the settle swept it back to the ridge
|
|
67
|
+
- Sounds played or changed with a volume, rate or pan of NaN or Infinity, as a sound at the camera in a hidden page, play silent, at the normal rate or centered in a release build, where the browser's audio threw
|
|
68
|
+
- Box2D in a release build takes NaN or Infinity in a joint's force, torque, frequency, limits, a mass and a pulley ratio of 0 as the nearest value it can take, makes no joint of an object to itself and no fixture of an infinite box or circle (addCircle returns undefined then), where the wasm stopped for good
|
|
69
|
+
- UI: two quick taps in frames one after the other click twice, and a press over a button whose release was missed in a hitch releases and clicks it first, where a click and a release were lost; a held button let go of by the window losing focus is released, not clicked
|
|
70
|
+
- A tween whose easing throws is ended, as one whose callback throws is
|
|
71
|
+
- Shader.dispose() in the frame it drew in draws that batch first, where the batch compiled it again and kept it
|
|
72
|
+
- tileCollisionRaycast on a layer away from the origin gives a hit inside the tile it met, where it could land on its edge, in the empty tile beside it
|
|
73
|
+
- writeSaveData with data whose JSON is nothing, as a toJSON that returns nothing, writes nothing and says so, where it stored the text undefined
|
|
74
|
+
- A lens flare whose sun or light is hidden when it is made starts hidden, where it showed at full and faded out over its fade time
|
|
75
|
+
- A moving particle emitter, 2D or 3D, spreads each update's particles along its move since the last one, where they came out in a clump at where it is, so a fast emitter leaves an even trail instead of beads; a jump further than twice objectMaxSpeed in one update, a teleport or a respawn, spreads nothing, so an emitter moved by hand faster than that, as one following the mouse, is not spread either, and a local space emitter is unchanged
|
|
76
|
+
- A sound whose file fails to load calls its onloadCallback too, with isLoaded false, so a game counting its sounds before it starts no longer waits for ever on a missing file; a callback written for success checks isLoaded()
|
|
77
|
+
- The speed cap, objectMaxSpeed, is applied after damping and gravity, 2D and 3D alike: each axis of a colliding object's move is within it, gravity included, where a falling object moved the cap plus its gravity a frame; what does not collide is not capped, a velocity over the cap keeps the capped value, and a cap of 0 holds a colliding 3D object still as it does a 2D one
|
|
78
|
+
- Audio: a one shot paused after its end and before its ended event stays at its end, where a resume played it again; a zzfx array's own randomness is kept to 0 to 1, as one passed in is; a sound at exactly its range, 2D or 3D, is out of it, not a silent voice
|
|
79
|
+
- particleEffect: an option that is not a value of its kind keeps the effect's own value, saying so in a debug build, where it quietly became the library's default ({speed: null} on fire, an emitTime of '0.5' that never stopped); a vec2 emitSize is a rectangle, as ParticleEmitter takes one; an option of no name asserts in a debug build
|
|
80
|
+
- A one shot particle effect gives the same count whichever frame it is made on, 1 at once and its rate for its emit time, where the rounding of time decided its last frame (an explosion gave 61 or 71); a burst shorter than a frame gives its rate for that time, and 2D and 3D emitters agree
|
|
81
|
+
- Image fonts and 3D text read text as a reader counts characters: an emoji, joiners and all, is one box where it was two to eight, and a letter with a combining accent draws as its letter; text with Windows line endings breaks lines as `\n` does in image fonts, canvas text, 3D text and the UI, where the `\r` drew a box or a space and shifted the line
|
|
82
|
+
- A font the canvas does not take, as a family name with spaces and digits like Press Start 2P left unquoted, warns once in a debug build, where the text drew as 10px sans-serif with no word; setFontDefault says to quote such names
|
|
83
|
+
- setSoundVolume asserts its volume is a number, and a NaN one is not handed to the audio, where it threw every frame; setSoundEnable says it is a setting for before sounds are made, setSoundVolume(0) the way to mute while playing
|
|
84
|
+
- 3D solids meet as in 2D: a fast object hitting a thin solid, or two solid objects meeting, are pushed back to the sides they came from, by where both were when the frame began, where the least overlap sent one on out the far side once past the other's middle; it holds the same whichever was made first and whichever of the two moves, a moving platform too, up to about the speeds 2D holds; turned boxes still push by least overlap. A 3D object shoved into a wall by another is settled against it the same frame, through the wall's collideWithObject, and the other takes what it could not move, so a pusher stops against a crate held by a wall and a heavy object resting on a light one no longer presses it into the floor
|
|
85
|
+
- A 3D particle crossing a ridge of a height map, both ends of its move above the ground, hits it, where only the ends were checked and it passed through; a move over several slopes hits the first it meets, and one leaving the map hits before it leaves
|
|
86
|
+
- A glTF model's parts, an attached prefab's parts and a voxel map's transparent blocks draw with the settings set on the model, handle or map (emissive, reflectivity, shader, shadows, renderAfter2D, renderOrder and the rest), each part keeping its own where the owner's is left at the default, so an unlit or rough glTF part keeps its file's material, and a part keeps its own values as its own (a part taken off draws with them); they kept the defaults
|
|
87
|
+
- Level editor: a reload of the file a Save wrote drops the autosave the file now has, and does not add an empty Objects layer, where an Objects layer the editor made for an object taken back again kept its empty list in the autosave; an Objects layer a Save wrote stays when its objects go after
|
|
88
|
+
- In a release build, the 3D renderer or the light system whose shaders do not build on a device says so once and draws nothing, where it drew every frame with programs that failed, and a device that fell back to Canvas2D is told 3D can not be drawn
|
|
89
|
+
- Tile collision stops an object against a wall or a ceiling, as it already did on a floor, so a game sees objects stop up to a frame's move further on at walls and ceilings, where it went back to where it was the frame before and stopped short by up to a frame's move, a gap that never closed for a game setting its velocity every frame; a bounce starts from the tile too, and a box landing with its top on a grid line no longer snaps into the tile above it
|
|
90
|
+
- A 3D particle emitter that outlives a scaled or turned parent keeps the parent's scale and turn, where it kept only its place and its particles fell at a quarter of the speed under a parent scaled by four; a particle's create callback reads the emitter's scale when it is emitted by hand before the first update or from inside another callback
|
|
91
|
+
- A UI object moved to another parent or detached in its own update is updated once that update, where it could be updated twice
|
|
92
|
+
|
|
93
|
+
### New Demos
|
|
94
|
+
|
|
95
|
+
- [3D Reflections](https://killedbyapixel.github.io/LittleJS/examples/?example=3D%20Reflections) - A chrome ball reflecting the scene around it, captured live, and balls from polished to rough under a sky painted in code
|
|
96
|
+
|
|
97
|
+
## 1.25.0 - 2026-10-04
|
|
98
|
+
|
|
99
|
+
### New Changes
|
|
100
|
+
|
|
101
|
+
- glTF skinned animation: a rigged character plays its animations, its mesh bent by its joints each frame, the four strongest of up to eight a vertex, on the CPU, so shadows, picking and custom shaders work with it; `object.play(name, loop, speed, blend)` cross-fades from the pose it is in over `blend` seconds, and `object.getJointMatrix(name)` gives a joint's place to hang a sword on a hand
|
|
102
|
+
- Tilt shift and depth of field post effects: `postProcessTiltShift(focus, size, blur)` keeps a band across the screen sharp, in 2D or 3D, and `postProcessDepthOfField(focus, range, blur)` keeps what is at a distance sharp, reading the 3D depth, with the edges of what is in focus kept crisp; the new 3D Focus Blur example focuses either with the mouse
|
|
103
|
+
- `postProcess.values`: a game's own values for the post process shader, set every frame, and any effect setting may name one in place of a number, to change it live without making the shader again, all but glow's size; the shader is made at the first render, so values set right after the plugin is made are in it
|
|
104
|
+
- The 3D Mesh example steps through a dropped model's animations with two buttons or the arrow keys, each cross-faded from the one before
|
|
105
|
+
|
|
106
|
+
### Breaking Changes
|
|
107
|
+
|
|
108
|
+
- The names deprecated in 1.20 are gone: `collideTiles` is `collideLevel`, the weld joint's `setSpringDampingRatio` and `getSpringDampingRatio` are `setDampingRatio` and `getDampingRatio` (the wheel joint keeps its own), and `NewgroundsPlugin.logView()`, which did nothing, is removed
|
|
109
|
+
- `drawNineSliceScreen` and `drawThreeSliceScreen` take only the order since 1.20, a color after the tile and then the border size; a number in the color's place asserts in a debug build
|
|
110
|
+
|
|
111
|
+
### Fixes
|
|
112
|
+
|
|
113
|
+
- REFERENCE says where the draw functions' parameter orders differ, and has a section on what functions give back: what each raycast returns, overlaps and collide helpers, not found, how a failed load is reported and how each kind of thing is ended; the FAQ lists the global names a game's own top level declarations clash with in a script build
|
|
114
|
+
- REFERENCE has a section on units and directions (angles in radians, clockwise in 2D, what is per frame and what is in seconds), says which vector and color methods give a copy, and names parameters as the code does; the Box2D shape methods, the vector helpers and a few others say what they return
|
|
115
|
+
- In a browser with no audio, a zzfx sound and the page hiding do nothing, where they threw
|
|
116
|
+
- A resume of the audio the browser refuses is caught, where it showed as an uncaught error in the console
|
|
117
|
+
- `debug`, `debugOverlay` and `debugWatermark` are `false` in a release build, as their types say, where they were 0
|
|
118
|
+
- `Box2dPinJoint`'s docs say its point defaults to objectA's position, which the d.ts showed as objectB's
|
|
119
|
+
- TypeScript: `tileLayersLoad` and `objectLayersLoad` take a `TiledMap`, `level3DLoad` a `Level3D`, the types' defaults and properties are records, a tool's `pos` and `ray` and `onPlayFrom`'s position are typed, `levelEditor.open` and `close` return nothing, `Sound` takes a zzfx array as numbers, and `fetchJSON` gives `any`, as JSON is, so its result goes straight into any of them
|
|
120
|
+
- A JSON, glTF or particle effect file that fails to read names the file, and says when it is a web page, as a dev server sends for a mistyped path, where it gave only "Unexpected token '<'"; an OBJ file with no faces warns, naming the file
|
|
121
|
+
- In a release build `levelEditor.edit2D` is there and changes nothing, where it was undefined and a call on it threw, so a game needs no guard around it
|
|
122
|
+
- A shader that fails to build on a device says why in the console in a release build too, where the screen stayed blank with no message: when the engine's own fails, the game draws with Canvas2D, a game's `Shader` that fails draws as if it had none, and a post process that fails leaves the frame as it is
|
|
123
|
+
- The post process depth is read at full precision, where phones with Mali and Adreno GPUs read it at 11 bits and the outline drew false lines on flat ground; the outline draws nothing with no depth texture, and nothing wrong at the sky of a camera with no far plane, where both gave NaN pixels; a value or a setting may not be named as a GLSL word or a name the shader uses, which was a compile error or a value silently hidden; a setting below .01 keeps its digits
|
|
124
|
+
- The 3D shader gives a mesh flattened to nothing on an axis no normal, where it was NaN, keeps the reflection's Fresnel term from NaN specks where a surface is seen head on, and treats fog that starts where it ends, and a shininess of 0, as defined
|
|
125
|
+
- `lineTest`, and the tile raycasts on it, from a whole x or y heading down the grid start in the cell they head into, where they tested the cell behind first and could hit at the start
|
|
126
|
+
- A particle whose `gravityScale` makes it rise lands on a ceiling, where its ground was the way the world's gravity points
|
|
127
|
+
- `readSaveData` takes only an object from storage, where a stored string was spread into its letters
|
|
128
|
+
- Faster where a game grows: path smoothing on a winding path checks lines from each corner, not each cell, a tween stopped among many no longer searches the list, a CSG cut leaves what is far from it alone, so a mesh cut many times does not grow slower to cut, and a paint stroke in the level editor draws the cells it changed, the whole layer only for a game's `onTile` or its own `onRedraw`
|
|
129
|
+
- Examples: the breakout tutorial runs at every step, the platformer's debug explosion is on B, not the dodge key, the Box2D example's smoke rises and its explosion falls off, the raycaster has no fisheye, the 3D Mesh viewer's normals leave a rigged model alone, positions are copied where they were shared, and a list of smaller fixes across the shorts
|
|
130
|
+
- An object whose child's update throws leaves the engine's list of children being updated as it was, where each throw kept them on it for good
|
|
131
|
+
- Level editor: a turned or mirrored stamp turns or mirrors its objects too, each object's rotation with it, a box placed from the corner that becomes its own, a polygon's points and a tile object's image mirrored
|
|
132
|
+
|
|
10
133
|
## 1.24.3 - 2026-10-03
|
|
11
134
|
|
|
12
135
|
### Fixes
|
package/EDITOR.md
CHANGED
|
@@ -22,8 +22,8 @@ is run by `test/editorCustom.test.mjs` straight from this file, so it works as w
|
|
|
22
22
|
and release are ended for you.
|
|
23
23
|
5. **A type is named by a string**, never by its class, because minified builds rename classes.
|
|
24
24
|
6. **Use `levelEditor.edit3D` and `levelEditor.edit2D` only inside what the editor calls**: a key's action, a
|
|
25
|
-
button's click, a tool's callbacks, `onUpdate`, `onDraw`. In a release build
|
|
26
|
-
|
|
25
|
+
button's click, a tool's callbacks, `onUpdate`, `onDraw`. In a release build none of those are ever called;
|
|
26
|
+
`edit2D` is there and changes nothing, and `edit3D` is undefined, as it is in a game without the 3D plugins.
|
|
27
27
|
7. **`levelEditor.is3D`** says which editor is in use, for a game that has both kinds of level.
|
|
28
28
|
|
|
29
29
|
## The hooks
|
package/FAQ.md
CHANGED
|
@@ -10,7 +10,10 @@ If you don't find an answer here, feel free to ask the community or check the do
|
|
|
10
10
|
- [How do I use LittleJS as an ES module?](#how-do-i-use-littlejs-as-an-es-module)
|
|
11
11
|
- [How do I use LittleJS with TypeScript?](#how-do-i-use-littlejs-with-typescript)
|
|
12
12
|
- [Why do I see a blank screen when I run my game?](#why-do-i-see-a-blank-screen-when-i-run-my-game)
|
|
13
|
+
- [Why does my game say a name "has already been declared"?](#why-does-my-game-say-a-name-has-already-been-declared)
|
|
13
14
|
- [Do I need a local server to run LittleJS games, and how do I set one up?](#do-i-need-a-local-server-to-run-littlejs-games-and-how-do-i-set-one-up)
|
|
15
|
+
- [Which browsers does LittleJS run in?](#which-browsers-does-littlejs-run-in)
|
|
16
|
+
- [How far can my world go, and how long can my game run?](#how-far-can-my-world-go-and-how-long-can-my-game-run)
|
|
14
17
|
- [How does the camera and world coordinate systems work?](#how-does-the-camera-and-world-coordinate-systems-work)
|
|
15
18
|
- [How do I configure engine settings?](#how-do-i-configure-engine-settings)
|
|
16
19
|
- [What plugins ship with LittleJS?](#what-plugins-ship-with-littlejs)
|
|
@@ -48,6 +51,7 @@ If you don't find an answer here, feel free to ask the community or check the do
|
|
|
48
51
|
|
|
49
52
|
**Debugging and Development**
|
|
50
53
|
- [How do I debug my game in LittleJS?](#how-do-i-debug-my-game-in-littlejs)
|
|
54
|
+
- [How do I record a smooth video of my game, like a trailer?](#how-do-i-record-a-smooth-video-of-my-game-like-a-trailer)
|
|
51
55
|
|
|
52
56
|
---
|
|
53
57
|
|
|
@@ -55,16 +59,16 @@ If you don't find an answer here, feel free to ask the community or check the do
|
|
|
55
59
|
### What is LittleJS, and how is it different from other JavaScript game engines?
|
|
56
60
|
|
|
57
61
|
LittleJS is a lightweight, high-performance JavaScript game engine designed for simplicity and speed.
|
|
58
|
-
It offers a hybrid rendering system that combines the advantages of WebGL and 2D Canvas, and
|
|
59
|
-
Unlike those feature-heavy engines, LittleJS
|
|
62
|
+
It offers a hybrid rendering system that combines the advantages of WebGL and 2D Canvas, and [bench/](bench/) runs the same scenes in it and in three.js, PixiJS and Phaser.
|
|
63
|
+
Unlike those feature-heavy engines, LittleJS keeps to a comprehensive set of simple, easy to use features, for 2D games first, with a 3D renderer of its own as well.
|
|
60
64
|
LittleJS is perfect for developers who want a minimal yet powerful engine to bring their 2D game ideas to life without the complexity of larger frameworks, and it's especially well suited for size-constrained competitions like js13kGames.
|
|
61
65
|
|
|
62
66
|
### How do I set up a basic LittleJS project?
|
|
63
67
|
|
|
64
|
-
|
|
68
|
+
Clone the LittleJS repository, or use Download ZIP on its GitHub page: the examples are only there, the npm package and the release zip hold the engine alone.
|
|
65
69
|
Include one of the LittleJS builds from the dist folder.
|
|
66
|
-
Several examples are included for you to build on.
|
|
67
|
-
The most basic example is just an empty project.
|
|
70
|
+
Several examples are included for you to build on, and they need a local web server: serve the repository's root, for example with `npx http-server`, and open `http://localhost:8080/examples/empty/` (see [Do I need a local server](#do-i-need-a-local-server-to-run-littlejs-games-and-how-do-i-set-one-up)).
|
|
71
|
+
The most basic example is just an empty project. Its `import '../../dist/littlejs.esm.js'` finds the engine from inside the repository, so a copy of the folder somewhere else changes that path to where the engine is.
|
|
68
72
|
|
|
69
73
|
[Empty Example HTML file:](https://github.com/KilledByAPixel/LittleJS/blob/main/examples/empty/index.html)
|
|
70
74
|
```html
|
|
@@ -132,7 +136,7 @@ LJS.engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost,
|
|
|
132
136
|
|
|
133
137
|
The empty example is an ES module, so engine names come from the `LJS` namespace. For a classic setup where the engine is a plain `<script src=../../dist/littlejs.js>` tag and every engine name is a global, start from [examples/starter](https://github.com/KilledByAPixel/LittleJS/tree/main/examples/starter) instead.
|
|
134
138
|
|
|
135
|
-
This is the simplest setup —
|
|
139
|
+
This is the simplest setup — three files served by a local web server. If you'd rather start from a bundler-based project with hot reload, see [How do I use Vite with LittleJS?](#how-do-i-use-vite-with-littlejs) for the official Vite starter template.
|
|
136
140
|
|
|
137
141
|
### How do I use LittleJS as an ES module?
|
|
138
142
|
|
|
@@ -193,8 +197,14 @@ See [examples/typescript/](examples/typescript/) for a complete TypeScript proje
|
|
|
193
197
|
|
|
194
198
|
If you are seeing a blank screen, first try opening the dev tools console (F12 in most browsers).
|
|
195
199
|
This will show you any errors that occur and allows stepping through code to help debug.
|
|
196
|
-
A common issue is the
|
|
197
|
-
|
|
200
|
+
A common issue is the game opened from a file on disk with no web server: the console then says an image was "blocked by CORS policy" from origin 'null' and `failed to load image: tiles.png`, or that a module script was blocked, and the game runs with no sprites or not at all.
|
|
201
|
+
Serve it from a local web server, see [Do I need a local server](#do-i-need-a-local-server-to-run-littlejs-games-and-how-do-i-set-one-up).
|
|
202
|
+
|
|
203
|
+
### Why does my game say a name "has already been declared"?
|
|
204
|
+
|
|
205
|
+
In a script tag build the engine's names are globals, so a top-level `let`, `const` or `class` in your game with the same name is an error, as is any declaration of a name the engine declares with `let` or `const`, like `time`. A `function` or `var` named like an engine function replaces it instead, which breaks the engine wherever it calls that function. The names most likely to clash are the short ones: `time`, `frame`, `paused`, `debug`, `gravity`, `tile`, `rand`, `percent`, `lerp`, `clamp`, `mod`, `sign`, `min`, `max`, `abs`, `floor`, `round`, `ceil`, `sin`, `cos`, `tan`, `PI`, the colors (`RED`, `GREEN`, `BLUE`, `GRAY`, `WHITE`, `BLACK` and the rest), `mousePos`, `cameraPos`, `medals`, `speak`, `vibrate`, `tweak`, the classes `Timer`, `Sound`, `Color`, `Light`, `Mesh`, `Particle` and `Shader`, and the plugin objects `box2d`, `render3D`, `uiSystem`, `postProcess` and `levelEditor`. [REFERENCE.md](REFERENCE.md) lists every name.
|
|
206
|
+
|
|
207
|
+
Rename yours, keep your game's state inside an object or class, or use the ES module build, where nothing is global and you import only the names you use (`import * as LJS from 'littlejsengine'` keeps them all under `LJS`).
|
|
198
208
|
|
|
199
209
|
### Do I need a local server to run LittleJS games, and how do I set one up?
|
|
200
210
|
|
|
@@ -219,6 +229,12 @@ There is also a function you can use called getCameraSize() to get the viewable
|
|
|
219
229
|
|
|
220
230
|
One thing to keep in mind: in **world space**, Y increases *upward* and tile coordinates use a bottom-left origin. This often surprises developers coming from Canvas2D or other 2D libraries. **Screen space** (used when you pass `screenSpace=true` to a draw function, or when reading `mousePosScreen`) follows the Canvas2D convention instead — Y increases *downward* with a top-left origin. Pick the right one for what you're doing: world space for gameplay, screen space for HUDs and UI overlays.
|
|
221
231
|
|
|
232
|
+
### How far can my world go, and how long can my game run?
|
|
233
|
+
|
|
234
|
+
The GPU draws with 32-bit floats, which hold about 7 digits, so precision runs out far from the origin and late in a long run. These are limits every engine has, not bugs, and most games never come near them:
|
|
235
|
+
- **Distance:** positions go to the GPU as they are, in world units, not relative to the camera. A 32-bit float steps by about .001 at 10,000 units from the origin and .008 at 100,000, so far out sprites and meshes start to jitter as they move. Keep the play area within about ±10,000 units, and a game that travels farther can move the world and the camera back toward the origin together now and then.
|
|
236
|
+
- **Time:** `time` itself is a 64-bit number and is fine, but shaders see it as the 32-bit `iTime`, which steps by about .0002 seconds after an hour and .008, half a frame, after a day, so a shader animated by it gets choppy in a game left running for days. A post process can be given a time of its own that wraps, through `postProcess.values`.
|
|
237
|
+
|
|
222
238
|
### How do I configure engine settings?
|
|
223
239
|
|
|
224
240
|
LittleJS exposes settings through `setX()` functions for every tunable value. Most settings need to be applied **before** `engineInit()` because they affect canvas creation, WebGL setup, or input registration:
|
|
@@ -251,7 +267,9 @@ Plugins are self-contained features that live alongside the engine but aren't pa
|
|
|
251
267
|
| Plugin | Purpose |
|
|
252
268
|
|--------|---------|
|
|
253
269
|
| `math3d.js` | `Vector3` and `Matrix4` for 3D games and plugins, with 3D collision and raycast helpers |
|
|
254
|
-
| `render3d.js` | The built-in 3D renderer:
|
|
270
|
+
| `render3d.js` | The built-in 3D renderer: the camera, lighting, shadows and fog, into the same canvas as the 2D scene (needs `math3d.js`) |
|
|
271
|
+
| `render3dMesh.js` | Meshes and the basic shape builders, `buildBox`, `buildSphere`, `buildGrid`, `buildLathe` and `buildSky` (goes after `render3d.js`) |
|
|
272
|
+
| `render3dObject.js` | `EngineObject3D`, `InstancedMesh3D` and the lights (goes after `render3dMesh.js`) |
|
|
255
273
|
| `render3dExtras.js` | The rest of the 3D toolkit on top of it: more shape builders, terrain, camera controls, 3D particles and trails, and the OBJ loader |
|
|
256
274
|
| `gltf.js` | Loads glTF and GLB models onto the 3D renderer: meshes, node placement and node animations, colors and textures |
|
|
257
275
|
| `threejs.js` | Renders a Three.js scene behind the LittleJS canvas, an alternative to `render3d.js` |
|
|
@@ -296,7 +314,7 @@ Key things the template sets up for you:
|
|
|
296
314
|
- A `public/.nojekyll` marker so GitHub Pages serves Vite's `_`-prefixed chunk files correctly.
|
|
297
315
|
- A full page reload on save instead of partial HMR, since LittleJS has engine-level global state (canvas, WebGL, input listeners, the RAF loop) that doesn't survive a module hot swap. The full reload is a small plugin in `vite.config.js`.
|
|
298
316
|
|
|
299
|
-
Requires Node
|
|
317
|
+
Requires Node 22.12+. Other community projects like [Michael Haynie's LittleJS Jam project](https://github.com/michael-dean-haynie/littlejs-game-jam-2024) are good real-world references for more elaborate Vite setups.
|
|
300
318
|
|
|
301
319
|
### How do I use Box2D with Vite?
|
|
302
320
|
|
|
@@ -378,7 +396,7 @@ LittleJS has a dedicated [js13k branch](https://github.com/KilledByAPixel/Little
|
|
|
378
396
|
- **Lean on built-ins to avoid asset files**: `Sound` with ZzFX parameters generates sound effects from a tiny array, and built-in shape drawing avoids shipping image assets.
|
|
379
397
|
- **Use a packer**: the js13k branch is set up to compress the final bundle with Roadroller or a similar packer. Check the branch's build script for the current toolchain.
|
|
380
398
|
|
|
381
|
-
For normal production (non-13KB) builds, the engine ships `dist/littlejs.release.js` (asserts stripped, about 1.
|
|
399
|
+
For normal production (non-13KB) builds, the engine ships `dist/littlejs.release.js` (asserts stripped, about 1.6 MB with every plugin and its comments) and `dist/littlejs.min.js` (minified, about 450 KB, about 135 KB gzipped). A bundler like Vite leaves out the plugins a game does not use, which makes a small game far smaller.
|
|
382
400
|
|
|
383
401
|
---
|
|
384
402
|
|
|
@@ -839,6 +857,32 @@ LOG('spawned enemy at', pos);
|
|
|
839
857
|
|
|
840
858
|
---
|
|
841
859
|
|
|
860
|
+
### How do I record a smooth video of my game, like a trailer?
|
|
861
|
+
|
|
862
|
+
A screen recorder such as OBS, the Xbox Game Bar or the macOS screen recorder is the quickest. For every frame at exactly 60 fps, even on a machine that cannot keep up, step the engine yourself and save each frame as an image: with `setEngineManualStep(true)` the game moves only when `engineStep` is called, one update of 1/60 of a second and its render, however long that takes.
|
|
863
|
+
|
|
864
|
+
```javascript
|
|
865
|
+
// record a number of seconds of the game, one image every 1/60 of a second
|
|
866
|
+
function recordFrames(seconds)
|
|
867
|
+
{
|
|
868
|
+
setEngineManualStep(true); // the engine moves only when engineStep is called
|
|
869
|
+
let frame = 0;
|
|
870
|
+
const next = ()=>
|
|
871
|
+
{
|
|
872
|
+
engineStep(1); // one update of 1/60 second, and its render
|
|
873
|
+
combineCanvases(); // the WebGL and 2D canvases as one image
|
|
874
|
+
saveCanvas(mainCanvas, 'frame' + String(frame).padStart(5, '0'));
|
|
875
|
+
if (++frame < seconds * 60)
|
|
876
|
+
setTimeout(next, 100); // a moment for the browser to save each one
|
|
877
|
+
else
|
|
878
|
+
setEngineManualStep(false); // back to real time
|
|
879
|
+
};
|
|
880
|
+
next();
|
|
881
|
+
}
|
|
882
|
+
```
|
|
883
|
+
|
|
884
|
+
The browser asks once to allow many downloads. The game plays slower than real time while it records, so this suits a scripted or replayed scene. Then make the video from the images, with ffmpeg for example: `ffmpeg -framerate 60 -i frame%05d.png -pix_fmt yuv420p trailer.mp4`.
|
|
885
|
+
|
|
842
886
|
## Contribute to the FAQ
|
|
843
887
|
If you have additional questions or think something should be added to this FAQ, please open an issue or pull request on the [LittleJS GitHub repository](https://github.com/KilledByAPixel/LittleJS).
|
|
844
888
|
|
package/README.md
CHANGED
|
@@ -52,6 +52,7 @@ The code is very clean and well documented with many examples to get you started
|
|
|
52
52
|
- Height map terrain and voxel maps with collision and raycasts
|
|
53
53
|
- 3D levels and prefabs, built in the 3D level editor and loaded with one call
|
|
54
54
|
- Shadow maps, colored lights, spotlights, lens flares, specular, emissive glow and fog
|
|
55
|
+
- Reflections of a sky box or the scene itself, from mirror sharp to rough
|
|
55
56
|
- Particles, trails, billboards and instanced drawing
|
|
56
57
|
- Orbit, chase and first person cameras with mouse picking
|
|
57
58
|
- Custom shaders on any object
|
|
@@ -74,6 +75,7 @@ The code is very clean and well documented with many examples to get you started
|
|
|
74
75
|
|
|
75
76
|
- Robust arcade physics system with collision handling
|
|
76
77
|
- Fast tilemap collision and raycasting
|
|
78
|
+
- Object collision that stays fast with thousands of objects
|
|
77
79
|
- Full Box2D integration for realistic physics using [Box2D v2.3.1 wasm](https://github.com/kripken/box2d.js)
|
|
78
80
|
- Grid-based A* pathfinding plugin with optional path smoothing
|
|
79
81
|
|
|
@@ -120,7 +122,9 @@ npm install
|
|
|
120
122
|
npm run dev
|
|
121
123
|
```
|
|
122
124
|
|
|
123
|
-
|
|
125
|
+
The examples are only in the repository: clone it, or use Download ZIP on its GitHub page; the npm package and the release zip hold the engine alone. A page that loads images or modules needs a local web server, as browsers block them from a file on disk: serve the repository's root, for example with `npx http-server`, then open `http://localhost:8080/examples/starter/`, the place to start. The [FAQ](FAQ.md#do-i-need-a-local-server-to-run-littlejs-games-and-how-do-i-set-one-up) has more.
|
|
126
|
+
|
|
127
|
+
Here is a minimal Hello World example game, with the script path pointing at the engine file from the download or npm package; it loads nothing, so it runs straight from the file.
|
|
124
128
|
|
|
125
129
|
```html
|
|
126
130
|
<!DOCTYPE html>
|
|
@@ -172,7 +176,7 @@ LittleJS comes with several demos both for learning and using as starter project
|
|
|
172
176
|
| [littlejs.esm.js](https://github.com/KilledByAPixel/LittleJS/blob/main/dist/littlejs.esm.js) | Debug | ESM | Debug mode with asserts |
|
|
173
177
|
| [littlejs.esm.min.js](https://github.com/KilledByAPixel/LittleJS/blob/main/dist/littlejs.esm.min.js) | Release | ESM | Optimized for release and minified |
|
|
174
178
|
|
|
175
|
-
Every build includes the plugins. littlejs.min.js is about
|
|
179
|
+
Every build includes the plugins. littlejs.min.js is about 135 KB gzipped, and the 3D plugins are a large part of that. A bundler like Vite leaves out the plugins a game does not use, so a small 2D game comes to about 26 KB gzipped, and a lit 3D scene to about 47 KB.
|
|
176
180
|
|
|
177
181
|
## Games Made With LittleJS
|
|
178
182
|
|