LittleJS Logo LittleJS - The Tiny JavaScript Game Engine That Can

Related tags

Web Game LittleJS
Overview

LittleJS Logo LittleJS - The Tiny JavaScript Game Engine That Can

All aboard!

LittleJS is lightweight 2D JavaScript game engine with a super fast WebGL rendering system. The goal of this project is to be small, simple, and easy to use for a variety of applications from size coding game jams to commercial releases. This engine has everything necessary for most games including super fast rendering, physics, particles, sound effects, music, keyboard/mouse/gamepad input handling, update/render loop, and debug tools. ๐Ÿš‚

LittleJS Screenshot

Features

  • Very small footprint with no dependencies
  • Can render 10,000+ objects at 60fps, several times more on a powerful machine
  • Object oriented system with base class engine object
  • 2D physics and collision handling for axis aligned boxes
  • Sound effects with zzfx and music with zzfxm, mp3s, or wavs
  • Input processing system with keyboard, mouse, gamepad, and touch support
  • Engine helper functions and classes like Vector2, Color, and Timer
  • Tile layer cached rendering and collision system for level data
  • Particle effects system (particle editor/designer in progress)
  • Several easy to understand example projects you can build on
  • Debug tools and debug rendering system
  • All example projects are compatible with mobile devices
  • Build system automatically combines everything, minifies, and removes unused code
  • For size coding competitions like js13kGames, starter project builds to a 6k zip
  • You can build as a native Windows App with electron for distribution on platforms like Steam
  • Open Source with the MIT license so it can be used to make commercial games

LittleJS Trailer

LittleJS Trello Roadmap

Example Starter Projects

Hello World - Clean project with only a few things to get you started

Puzzle Game - Match 3 puzzle game with HD rendering and high score tracking

Platformer - Platformer/shooter with procedural generation and destruction

Breakout - Breakout style game using pixelized 720p rendering

Stress Test - Max sprite/object test and music system demo

How to use LittleJS

It is recommended that you start by copying the LittleJS Starter Project It is mostly empty with just a few things you can use to get started or remove. You can also download and include engine.all.js or engine.all.min.js.

To startup LittleJS, you must create 5 functions and call engineInit. A canvas will automatically be created and added to the document. You can use this template to get started.

function gameInit()
{
    // called once after the engine starts up
    // setup the game
}

function gameUpdate()
{
    // called every frame at 60 frames per second
    // handle input and update the game state
}

function gameUpdatePost()
{
    // called after physics and objects are updated
    // setup camera and prepare for render
}

function gameRender()
{
    // called before objects are rendered
    // draw any background effects that appear behind objects
}

function gameRenderPost()
{
    // called after objects are rendered
    // draw effects or hud that appear above all objects
}

// startup LittleJS with your game functions after the tile image is loaded
engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost, 'tiles.png');

For most games you will want to extend EngineObject with your own objects. This will create an object class called GameObject and the constructor automatically adds it to the list of objects. Engine objects are automatically updated and rendered until they are destroyed.

You can override these functions to make objects behave however you want. See the examples for a complete demonstration.

class MyObject extends EngineObject 
{
    constructor(pos, size, tileIndex, tileSize, angle, color)
    {
        super(pos, size, tileIndex, tileSize, angle, color);
        // your object init code here
    }

    update()
    {
        super.update(); // update object physics and position
        // your object update code here
    }

    render()
    {
        super.render(); // draw object as a sprite
        // your object render code here
    }
}

Engine Configuration

All engine settings are listed in engineConfig.js. Here are the most important settings...

  • fixedWidth and fixedHeight - use a fixed canvas resolution, if not set uses native screen resolution
  • pixelated - disable blending in several places for pixel art style games
  • glOverlay - fix slow rendering in some browsers by not compositing the WebGL canvas
  • glEnable - run without WebGL but textured coloring is disabled and it is much slower
  • audioVolume - adjust volume of sounds and music

Games Made With LittleJS

  • Space Huggers - A more developed version of the platformer example
  • Send me your games!

LittleJS Logo

Comments
  • A bit of help with changing shaders appreciated

    A bit of help with changing shaders appreciated

    Hey,

    So I'm finally getting around to changing shaders, and I'm able to do it if I just change it "for the whole app" on runtime, but if I try to change shaders for rendering specific objects (i.e. just water tiles), this doesn't work. Basically the browser just hangs, so I'm assuming I'm doing/reiniting something that I shouldn't be when changing shaders, but it's really not obvious to me -> this is first time I'm trying to use them so I'm probably missing something super obvious about how webGL works, any chance you could have a quick look at my below code?

    From some webGL examples from what I understand when changing shaders after useProgram, buffers and vertexAttribs should be re-inited for the shared you're using, but it seems this is causing some sort of overkill when done hundreds of times per frame - do you see something obvious I should just need to remove/Change a bit to get this working? And sorry for wasting your time with what I assume are some of the webGL basics ๐Ÿ˜…

    //////// engineWebGL.js changes
    
    let shaders = { // just defining some different shaders
    	base: {
    		vs: // vs1 string
    		fs: // fs1 string
    	}
    	custom: {
    		vs: // vs2 string
    		fs: // fs2 string
    	}	
    };		
    		
    let customGLShader;
    let usingShader = 0, glVertexData;
    
    function initShaderVertextAttribArray() { // this is essentially moved from glInit, to be able to reinit when I change a shader
    
        // init buffers
        glCreateBuffer(gl_ARRAY_BUFFER, glVertexData.byteLength, gl_DYNAMIC_DRAW);
    
        // setup the vertex data array
        let offset =0;// glBatchCount = 0;
        const initVertexAttribArray = (shaderRef, name, type, typeSize, size, normalize=0)=>
        {
            const location = glContext.getAttribLocation(shaderRef, name);
            glContext.enableVertexAttribArray(location);
            glContext.vertexAttribPointer(location, size, type, normalize, gl_VERTEX_BYTE_STRIDE, offset);
            offset += size*typeSize;
        };
    
        let program = (usingShader === 0 ? glShader : customGLShader);
        initVertexAttribArray(program, 'p', gl_FLOAT, 4, 2);            // position
        initVertexAttribArray(program, 't', gl_FLOAT, 4, 2);            // texture coords
        initVertexAttribArray(program, 'c', gl_UNSIGNED_BYTE, 1, 4, 1); // color
        initVertexAttribArray(program, 'a', gl_UNSIGNED_BYTE, 1, 4, 1); // additiveColor
    }
    
    function glInit() {
    	... //unchanged up to below point 
    
    	// setup vertex and fragment shaders
    	glShader = glCreateProgram(
    		shaders.base.vs                      // end of shader
    		,
    		shaders.base.fs
    		// end of shader
    	);
    
    	customGLShader =  glCreateProgram(
    		shaders.custom.vs
    		,
    		shaders.custom.fs
    	);
    
    	glVertexData = new ArrayBuffer(gl_MAX_BATCH * gl_VERTICES_PER_QUAD * gl_VERTEX_BYTE_STRIDE);
    	glPositionData = new Float32Array(glVertexData);
    	glColorData = new Uint32Array(glVertexData);
    
    	glBatchCount = 0;
    	initShaderVertextAttribArray();
    }
    function glPreRender() {
    	// everything is the same, instead of using glShader directly, its using this:
    	
        let program = (usingShader === 0 ? glShader : customGLShader);
    }
    
    
    
    ////// in-game changes:
    function changeToShader(shaderName) {
        usingShader = shaderName;
        initShaderVertextAttribArray();
    }
    
    
    // When rendering an object, I just try to switch shader in a certain situation:
    
    render() {
    	if (this.tileIndex >= 256 && this.tileIndex < 320) { // change onyl for certain i.e. water tiles
    		changeToShader(1);
    	}
    	// render stuff here
    	
    	
    	if (this.tileIndex >= 256 && this.tileIndex < 320) {
    		changeToShader(0); // change back to original/default shader
    	}
    }
    
    opened by slosumo 8
  • Drawing images from texture that are not following the

    Drawing images from texture that are not following the "tileIndex rule"

    From what I've looked at this is currently unsupported, or at least can't see a "neat way" to draw an image from the texture, that is NOT following the tileSize and tileIndex "rules".

    For example, say I do want to use tiles in most cases, but simply have some objects to draw which are much bigger than the tileSize (and the image has to be bigger, or even not a square, but rectangular (use case can only be inside render functions of objects, and no need to be supported usage by tileLayers).

    How could I achieve this to use gl renderer, and be able to use "custom "tileImage spriteCut" parameters", i.e.: sx ... x of which area of the texture to take sy ... y ^^ sw ... w ^^ sh ... h ^^

    Would the simplest be to adjust the drawTile tileIndex parameter, that if it's passed as object of sx,sy,sw,sh values, just calculate uvX, uvY differently? I'm not sure if that's all that's needed, as I didn't dive yet into what all the other params of bleed etc. mean.

    Any help/point in the right direction much appreciated!

    texture_indexes_questions

    opened by slosumo 8
  • Redrawing a single tileData after changing its data

    Redrawing a single tileData after changing its data

    While this is a more complex use case, I could imagine it be useful to a lot, to be able to do animations on tiles when needed, and use tilelayer rendering for them whenever possible for best performance.

    TLDR: I'm wondering if it's possible to change data of a tileLayer and redraw just that tile, instead of the whole layer (clearing works, but not redrawing, because of the assert check in drawTileData)

    While I could just remove the assert that feels wrong, but so does redrawing the whole layer. Is there something you'd suggest doing here? If this could be done without breaking "the laws of littlejs", it would be an amazing performance improvement to run a ton of temporary animations on tiles when needed

    Below is my code, which I intend to later use for basically doing animations on tiles (where I'll temporarily unset tile data, and render animation on the mainContext instead, then when animation ends, fallback to using tileData), but currently I'm just trying to get this working:

    let ongoingTileAnimations = []; // stores info about any ongoing tileAnimations, which are then checked and handled every frame
    
    function beginTileAnimation(data) {
        /* function that is used for animating stuff, i.e. if a tile has to be animated, we remove that tile from the tilelayer data,
            perform the animation in a new object we create on the mainCanvas,
            and when animation ends, we restore the original (or modified) tilelayer data at that position
         */
    
        /* ** signifies required params
        * Data:
        * pos/posTileIndex**
        * tileLayer**
        * animationData: duration**,
        * + any other data I might need for diff animations
        * */
    
        data.startedAt = realTime;
        data.endAt = realTime + data.animationData.duration;
        if (data.tileLayer) {
    
            data.originalTileData = tileLayers[data.tileLayer].getData(data.pos);
    
            tileLayers[data.tileLayer].setData(data.pos, new TileLayerData, 1); // set and clear tile
        }
        ongoingTileAnimations.push(data);
    }
    
    function processTileAnimations() { // this function is ran from gameUpdate(), and checks for any ongoing animations
        let animData;
        for(let i=ongoingTileAnimations.length-1; i >= 0; i--) {
            animData = ongoingTileAnimations[i];
    
            if (animData.endAt <= realTime) {
                /* for now, this examples just attempts to restore tileData after passed time, but here, the setData ASSERT error happens,
                * because the tile is attempting to render a tile, not just a color (in function drawTileData)
                */
                //console.log("animation ended here!, data: " + JSON.stringify(animData));
    
                tileLayers[animData.tileLayer].setData(animData.pos, animData.originalTileData, 1); // doesn't work <- assert error
    
                /* // this DOES work, but seems like overkill redrawing whole tileLayer
                tileLayers[animData.tileLayer].setData(animData.pos, animData.originalTileData);
                tileLayers.terrain_pri.redraw();
                */
    
                ongoingTileAnimations.splice(i, 1); // remove animationData
            }
        }
    }
    
    opened by slosumo 7
  • generate Typescript type declarations from sources and add them to the project

    generate Typescript type declarations from sources and add them to the project

    • Typescript is added as an optional dependency
    • run npm run update-type-declarations to update the type declarations.
    • add a script to generate the type declarations from the JSDocs in the source files
    • Typescript users can import the declarations in a project by referencing them in a *.d.ts file via /// <reference types="..." />
    opened by martinemmert 5
  • Issue refreshing glDrawing?

    Issue refreshing glDrawing?

    I'm not sure if this is a bug, or I should be doing something I'm not.

    I have an objectSpawner object, which renders a grid of tiles where you can place based on mouse position, which works like:

    1. user clicks UI, and it creates a ObjectSpawner object, and stores a global reference to "objectSpawner", so I can update it when moving mouse etc
    2. this objectSpawner has a custom render function that does something like this: for (var pl=0; pl < this.objectsData.length; pl++) { // draw above tileLayers where an object you have has certain range/grid coverage // get color, etc... drawTile(pos, this.drawSize, this.tileIndex, this.tileSize, this.color, this.angle, this.mirror); }

    Now this all works fine generally speaking when I place the object properly (which destroyed the object, sets reference of objectSpawner = false and then, it setsData in tileLayers which renders those after), but there's an issue in a situation where user cancels placement, and tileLayers data isn't changed/refreshed (.destroy and reference are cleaned as well), and it apparantely doesn't cause a redraw of the gl layer, and leave the "grid of tiles" still rendered.

    What is the correct way to "force a refresh" of the gl layer in such a situation? I currently can do something like this to force a refresh, but it seems hacky at best - is there a more proper way to do this?

    drawRect(vec2(1), 1, new Color(1,1,1,0), 0);

    opened by slosumo 5
  • ESM build

    ESM build

    I recently made a game using LittleJS and overall had a great experience. But since I like to use ES modules, it was a little awkward using the little engine because it doesn't expose the variables/methods it uses.

    I'm looking to build on the previous project, and I wanted to improve this experience so I did a proof of concept turning the engine.all.js file into a file that can be imported ESM-style. See code here: https://github.com/deathraygames/wisdom-of-the-ages/tree/main/src/little-engine-esm The way it works is that if you run little-builder.js it will just append the three files together: little-header.js, engine.all.js (latest from node modules), and little-footer.js. In the footer I'm exposing all the variables and methods, and had to add a few getter and setter methods. The resulting file is little-engine-esm-build.all.js.

    Work on the footer is not complete. To expand on this I would add get/set methods for all variables, and avoid exporting them directly. The downside of all of this is that makes the code somewhat larger: 177+ versus 172 KB. But to someone who loves ESM, that trade-off is worth it. Maybe someone has ideas of ways to make this smaller and/or to make the footer generation more automatic?

    Would you like to have this kind of ESM build included in the LittleJS repo? If so I can fork and do a PR.

    opened by Lukenickerson 4
  • Direction angle for particle emitter

    Direction angle for particle emitter

    1. I've spent a bit too much time than I'd like on this, figuring out how I can set the direction where the particle emmiter emits to, trying out all different combinations of params. Only to find out, I can easily do it by setting the particleEmitter.angle after creating it.

    Unless you dive into the code this is really unclear - imo it would make a lot of sense to add another clear parameter for "emitAngle" that allows users to easily control use.

    1. To add onto this, the current particles can only be of square shape -> unless if I used a tile image that was of rectangular shape. But if I want to just use colored particles that are shaped like a rectangle, it's not currently possible. Am I thinking in "the wrong way again", and there's no reason for this to be added, and I should just make a rectangular tile and use that one?
    opened by slosumo 4
  • Help using shaders

    Help using shaders

    I'm having a bit of a hard time wrapping my head around of how I could apply shaders to a specific layer or all of the canvases based on time, to i.e. be able to continually animate something (for example waves on water tiles) without having to resort to sprite animations which would require redrawing that said layer every frame.

    From what I've gathered so far:

    • applying a shader to the glCanvas is super straightforward -> anything using GL rendering every frame will simply work with a shader (example of that hover object in example)
    • applying shaders to tileLayer only works at the time when they are refreshed/redrawn (i.e. after I click to place an object, it redraws that tileLayer and applies the updated shader)

    So if I currently have say water tiles on a tileLayer, and I'd like to use a shader every frame on it, it would require a tileLayer.redraw() every frame, which feels like an overkill. Do you have an idea how something like this would be best to achieve in the current state of the engine?

    I assume your post-processing task when done would sort of allow someone to do this quite easily?

    I'm attaching an example that modulates the Blue in the shader based on time. The hover grids are drawn on gl, and everything below is are tileLayers - as you can see they are not changing color, until I place something on them (Which redraws those specific tiles):

    https://user-images.githubusercontent.com/10269798/142209764-d3a263ad-bab0-406a-ac7c-d553f7183e21.mp4

    opened by slosumo 4
  • "abusing renderOrder" potential performance issues?

    TLDR: Is it safe to use any amount of different render orders, or is it recommended for some sort of reason to stay under a certain amount?

    I'm wondering about something, and if it might cause potential performance issues -> I think not, as the way renderOrder works is just a sort, but just wanted to double check in case I'm missing some intricacy of how it all works under the hood.

    Essentially, if I have objects with the same renderOrder, they will be rendered in order of their creation, which can cause situation like this: (top tree being rendered on top): image

    Now this is easily solvable, if I give every tree a different render order, based on their position (i.e. rendering goes from higher pos.y down to the lowest, and from lowest pos.x to the highest), but i.e. if I had a map of 50x50 then that's potentially 2500 different render orders. I suppose that shouldn't be an issue, as the render order is just a matter of sorting the integers?

    Essentially I want to render objects on different layers (but without using tileLayers as they're often animated etc), and I could keep diff objects layered properly, and avoid these render issues like so: Say on a 10x10 map, I have undergrowth & trees layer, I could then say that all:

    Undergrowth have a renderOrder of  0 + (100 - (pos.x + levelSize.x * pos.y)) =  (0 - 99)
    Trees have a base a renderOrder of 100 + (100 - (pos.x + levelSize.x * pos.y)) (100 - 199)
    

    And this should work with basically any map size and any amount of different layers (besides obviously hardware limit of how many can be rendered at the same time)

    opened by slosumo 3
  • Use ES module or CommonJS ?

    Use ES module or CommonJS ?

    Thank you very much for providing this library, I am going to read the source code through Hello World, I know that you don't want to release NPM versions, each JS file is interactive through a lot of global variables, so it has caused a lot of doubts, which JS file from these global variables?

    I think it will be easier to understand the principle of this library if we organize the code (such as import or require) in combination with the modularization of JS

    opened by zhw2590582 3
  • Debug mode information/legend

    Debug mode information/legend

    Would appreciate an additional legend to be added to debug overlay explaining the various colors/squares: image

    I assume one of the blues is showing world pos, 1 is showing render pos of the tile or something like that? I could decipher it but a quick glimpse at what these things mean would be great to haves!

    opened by slosumo 3
  • Display currently possible moves

    Display currently possible moves

    Every time the board is updated, the currently possible moves are calculated and shown on the dashboard.

    Not a necessary addition, but it kind of keeps me less frustrated. Take it if you see deem it useful.

    opened by hartmutobendorf 0
  • bug: touchend event problem.

    bug: touchend event problem.

    The touchend event seems to be cancelable on mobile phones.

    So the following code will prevent the page focus back sometime.

    // try to enable touch mouse
    if (isTouchDevice)
    {
        // handle all touch events the same way
        let wasTouching, hadTouchInput;
        ontouchstart = ontouchmove = ontouchend = (e)=>
        {
            e.button = 0; // all touches are left click
    
            // check if touching and pass to mouse events
            const touching = e.touches.length;
            if (touching)
            {
                hadTouchInput || zzfx(0, hadTouchInput=1) ; // fix mobile audio, force it to play a sound the first time
    
                // set event pos and pass it along
                e.x = e.touches[0].clientX;
                e.y = e.touches[0].clientY;
                wasTouching ? onmousemove(e) : onmousedown(e);
            }
            else if (wasTouching)
                onmouseup(e);
    
            // set was touching
            wasTouching = touching;
    
            // prevent normal mouse events from being called
            return !e.cancelable; // ๐Ÿ’ฅ touchend return false
        }
    }
    
    opened by akira-cn 2
  • Docs: search dropdown shows wrong return types for some functions

    Docs: search dropdown shows wrong return types for some functions

    For example, if I search for "utilities.abs" on https://killedbyapixel.github.io/LittleJS/docs/Input.html:

    image

    abs is listed as returning undefined, but it actually returns a Number.

    opened by tbroadley 1
Releases(v1.4.0)
Owner
Frank Force
Livelong game developer, designer, coder, artist, musician, Buddhist, and friend to cats.
Frank Force
A tiny game to practice AoE building shortcuts.

Aegis A tiny game to practice Age of Empires IV building shortcuts. Using โš›๏ธ Create-React-App and ?? Zustand. License With the exception of all visual

Alexander Widua 30 Jan 1, 2023
A lightweight 3D game engine for the web.

A lightweight 3D game engine for the web. Built with three.js and cannon-es.

null 667 Dec 31, 2022
Excalibur.js 1.3k Dec 30, 2022
WordleGameCB - a game inspired by the Wordle word game developed by Josh Wardle

Technologies WordleGameCB WordleGameCB is a game inspired by the Wordle word game developed by Josh Wardle. This application was developed with the ai

@Encoding 4 Feb 20, 2022
A super generic Lua web engine. Uses Lua for front-end, JavaScript for back-end, implemented in HTML.

A super generic Lua web engine. Uses Lua for front-end, JavaScript for back-end, implemented in HTML. This project is still in HEAVY development and w

Hunter 2 Jan 31, 2022
Snake game using pure HTML, CSS and JavaScript with GameBoy look and feel using Nano editor

Snake game using pure HTML, CSS and JavaScript with GameBoy look and feel using Nano editor. 100% commented code in Portuguese

Gabriel Martins 2 Jul 2, 2022
The Snake Game implemented using HTML, CSS, and JavaScript

The Snake Game implemented using HTML, CSS, and JavaScript

Wissam Fawaz 3 Mar 2, 2022
A simple Sokoban game made in JavaScript + HTML + CSS

About Sokoban is a puzzle video game genre in which the player pushes crates or boxes around in a warehouse, trying to get them to storage locations.

Juan Manuel Flecha 4 May 24, 2022
Hangman Game in JavaScript v2.3

Hangman Game Hangman Game in JavaScript Hangman is a paper and pencil guessing game for two or more players. One player thinks of a word, phrase or se

Nemonet 5 Jul 27, 2022
JavaScript library guessing game, a Wordle clone

Jazle JavaScript library guessing game, a Wordle clone. A fork of cwackerfuss/react-wordle. See the list of accepted libraries: wordlist.ts Build and

Oliver Radwell 17 May 26, 2022
Fill 100 squares game, made in JavaScript

Fill 100 Game Fill 100 squares game, made in JavaScript How it works The game needs a 10x10 square, but you can choose any size. Every cell is filled

Matteo Bruni 4 Mar 5, 2022
๐ŸŽ Open source racing game developed by everyone willing

?? Open source racing game developed by everyone willing

Poimandres 2k Dec 26, 2022
Golf it! is a game designed to let you show off your code-fu by solving problems

Golf it! Golf it! is a game designed to let you show off your code-fu by solving problems in the least number of characters โœจ Explore the docs ยป View

Ashikka Gupta 5 Aug 18, 2022
Pig is a simple two player dice game.

Pig game Pig is a simple two player dice game. Play here: formidablae.github.io/pig_game or formaidablae-pig-game.netlify.app Each turn, a player repe

null 10 Oct 5, 2022
Quizpetitive - A quiz game to learn new knowledge and terms in the field of project management.

Quizpetitive A quiz game to learn new knowledge and terms in the field of project management. The key element to the success of this project was the c

LMF 1 May 16, 2022
Bitburner Game

Bitburner Bitburner is a programming-based incremental game that revolves around hacking and cyberpunk themes. The game can be played at https://danie

null 2.6k Dec 30, 2022
A clone of the popular Wordle game.

Wordle Clone How to play locally: yarn install

null 2 Jan 9, 2022
A clone of the popular game Wordle made using React, Typescript, and Tailwind

Wordle Clone Go play the real Wordle here Read the story behind it here Try a demo of this clone project here Inspiration: This game is an open source

Hannah Park 2.4k Jan 8, 2023
๐ŸŽฉ 2048 game is cloned with ReactJS, CSS3.

2048-react This is a clone of 2048 implemented using React. It's running live here. The game logic is implemented in ./src/components/mainBoard.jsx. I

Arham 7 Jul 31, 2022