LittleJS is a tiny, zero-dependency HTML5 game engine in a single JavaScript file. It uses WebGL for rendering and ships with input, audio, physics, particles, and tilemap helpers — all in a few dozen KB.
LittleJS works with any JavaScript bundler — Vite, Webpack, Rollup, esbuild, Parcel, and others. This page covers the generic setup. The example repo uses Vite.
Install dependencies
LittleJS 1.26 and later includes a Wavedash plugin, so the engine is the only package you need.
npm install littlejsengineThe plugin reads the SDK that Wavedash provides on the page, and every call does nothing when the game runs anywhere else.
Wire up the plugin
Create a WavedashPlugin at the end of gameInit. It calls Wavedash.init() for you, so don't call it yourself.
Creating the plugin is required. Your game stays hidden behind the Wavedash loading screen until Wavedash.init() runs. Create it once your game is ready to play.
import { engineInit, medalsInit, WavedashPlugin } from 'littlejsengine';
function gameInit() {
// Your game's one-time setup.
medalsInit('My Game');
new WavedashPlugin();
}
function gameUpdate() { /* per-frame logic */ }
function gameUpdatePost() { /* after physics */ }
function gameRender() { /* WebGL draw */ }
function gameRenderPost() { /* UI / overlays */ }
engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost, []);Achievements
A WavedashMedal is a LittleJS medal tied to a Wavedash achievement by its identifier.
import { WavedashMedal } from 'littlejsengine';
const medalFinish = new WavedashMedal(0, 'ACH_01_FINISH', 'Finish', 'Finish a level');
medalFinish.unlock();On Wavedash, unlock() sets the achievement and Wavedash shows its own toast; elsewhere it shows the engine's medal popup. Create each achievement first with wavedash achievement create, using the same identifier.
Leaderboards
Pass your leaderboards to the plugin by name, and each one is created on first launch.
import { WavedashPlugin, wavedash } from 'littlejsengine';
new WavedashPlugin({
LEVEL_1: { lowerWins: true, display: 'milliseconds' },
HIGH_SCORE: {},
});
await wavedash.postScore('LEVEL_1', timeMs);
const entries = await wavedash.getScores('HIGH_SCORE', 0, 10);lowerWins defaults to false, and display is one of 'number' (the default), 'seconds', 'milliseconds', or 'ticks'. Scores are rounded to whole numbers, and Wavedash keeps each player's best.
If you minify with your own build step, keep the SDK's property names and real booleans (for example, disable Terser's booleans_as_integers). The SDK throws on renamed calls or 1 in place of true.
Other SDK features
For SDK features the plugin doesn't wrap, like players, cloud saves, and stats, install the typed SDK package.
npm install @wvdsh/sdk-jsimport Wavedash from "@wvdsh/sdk-js";
const user = Wavedash.getUser();@wvdsh/sdk-js throws on import outside Wavedash, so run your game through wavedash dev when you use it.
wavedash.toml
Point upload_dir at your bundler's output directory. For Vite and most other bundlers that's ./dist.
game_id = "YOUR_GAME_ID_HERE"
upload_dir = "./dist"Local development
Produce a build, then run wavedash dev to serve it through the Wavedash sandbox.