Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 9 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ A modern & lightweight HTML5 game engine
-------------------------------------------------------------------------------
![melonJS](https://melonjs.org/img/alex4-github.png)

[melonJS](https://melonjs.org/) is an open-source 2.5D game engine designed for indie developers — perspective and orthogonal cameras, GPU-accelerated tilemap rendering, post-processing effects, custom shaders, 3D mesh support, polygon-accurate physics, modern Tiled workflows, and high performance. Runs on WebGL or Canvas2D with automatic fallback, tree-shakeable so you only pay for what you use, and the entire engine fits in ~150 KB minzipped of vanilla JS/TS with no toolchain lock-in. Built with ES6 classes and bundled with [esbuild](https://esbuild.github.io).
[melonJS](https://melonjs.org/) is an open-source 2.5D game engine designed for indie developers — perspective and orthogonal cameras, GPU-accelerated tilemap rendering, post-processing effects, custom shaders, 3D mesh support, polygon-accurate physics, modern Tiled workflows, and high performance. Runs on WebGPU, WebGL 2 or Canvas2D with automatic fallback, tree-shakeable so you only pay for what you use, and the entire engine fits in ~150 KB minzipped of vanilla JS/TS with no toolchain lock-in. Built with ES6 classes and bundled with [esbuild](https://esbuild.github.io).

[melonJS](https://melonjs.org/) is licensed under the [MIT License](LICENSE.md) and actively maintained by the team at AltByte in Singapore.

Expand All @@ -28,11 +28,11 @@ melonJS is designed so you can **focus on making games, not on graphics plumbing

- **[Canvas2D-inspired rendering API](https://github.com/melonjs/melonJS/wiki/Rendering-API)** — If you've used the HTML5 Canvas, you already know melonJS. The rendering API (`save`, `restore`, `translate`, `rotate`, `setColor`, `fillRect`, ...) follows the same familiar patterns — no render graphs, no shader pipelines, no instruction sets to learn.

- **True renderer abstraction** — Write your game once, run it on WebGL or Canvas2D with zero code changes. The engine handles all GPU complexity behind a unified API, with automatic fallback when WebGL is not available. Designed to support future backends (WebGPU) without touching game code.
- **True renderer abstraction** — Write your game once, run it on WebGPU, WebGL 2 or Canvas2D with zero code changes. The engine handles all GPU complexity behind a unified API: the default `AUTO` mode negotiates the best available backend (WebGPU → WebGL 2 → Canvas) at startup, and the entire feature set renders identically on both GPU backends.

- **Complete engine, minimal footprint** — Physics, tilemaps, audio, input, cameras, tweens, particles, UI — a full game stack in a single tree-shakeable ES module. No dependency sprawl, no library stitching.

- **Scenes, loaded in one call** — `level.load(name)` brings an authored scene straight into your world. [Tiled](https://www.mapeditor.org) is a first-class citizen for **2D** — orthogonal, isometric, hexagonal & staggered maps, animated tilesets, collision shapes, object properties, compressed formats, with GPU-accelerated tile rendering under WebGL 2 — and **glTF / GLB** is the equivalent for **3D scenes**: author in Blender (or any DCC tool), export a `.glb`, and the whole scene — meshes, materials, cameras, lights, and node animation — loads under a `Camera3d`, no per-mesh wiring. Animated models play back through the same animation API as a 2D `Sprite`.
- **Scenes, loaded in one call** — `level.load(name)` brings an authored scene straight into your world. [Tiled](https://www.mapeditor.org) is a first-class citizen for **2D** — orthogonal, isometric, hexagonal & staggered maps, animated tilesets, collision shapes, object properties, compressed formats, with GPU-accelerated tile rendering on the GPU backends — and **glTF / GLB** is the equivalent for **3D scenes**: author in Blender (or any DCC tool), export a `.glb`, and the whole scene — meshes, materials, cameras, lights, and node animation — loads under a `Camera3d`, no per-mesh wiring. Animated models play back through the same animation API as a 2D `Sprite`.

- **Batteries included, hackable by design** — Get started in minutes with minimal setup. When you need to go deeper: ES6 classes throughout, a plugin system for engine extensions, and a clean architecture that's easy to extend without fighting the framework.

Expand All @@ -46,7 +46,7 @@ Compatibility
- Compatible with all major browsers (Chrome, Safari, Firefox, Opera, Edge) and mobile devices

Graphics
- Fast WebGL renderer for desktop and mobile devices with fallback to Canvas rendering
- Fast GPU renderers (WebGPU and WebGL 2) for desktop and mobile devices, with fallback to Canvas rendering
- Extensible batcher system for custom rendering pipelines
- High DPI resolution & Canvas advanced auto scaling
- Sprite with 9-slice scaling option and frame animation
Expand All @@ -57,8 +57,8 @@ Graphics
- 3D mesh rendering with OBJ/MTL model loading, multi-material support, hardware depth testing, and perspective projection via `Camera3d` — ~30% faster mesh rendering with near-zero per-frame allocation (a re-drawn static mesh produces no GC garbage)
- Lighting, in 2D and 3D:
- **2D** — `Light2d` as a first-class `Renderable` (multiple dynamic lights, radial-gradient falloff, illumination-only mode, procedural rendering via `drawLight`), plus optional per-pixel normal-map shading on sprites for 3D-looking dynamic lights
- **3D** — `Light3d` directional + ambient lights, added to the world like `Light2d` (half-Lambert diffuse + ambient fill, runtime-manipulable for day/night), auto-loaded from a glTF scene's authored sun
- Built-in shader effects (Flash, Outline, Glow, Dissolve, CRT, Hologram, etc.) with multi-pass chaining via `postEffects`, plus custom shader support via `ShaderEffect` for per-sprite fragment effects (WebGL)
- **3D** — `Light3d` directional, point, spot and ambient lights, added to the world like `Light2d` (half-Lambert diffuse + ambient fill, runtime-manipulable for day/night), auto-loaded from a glTF scene's authored suns and lamps
- Built-in shader effects (Flash, Outline, Glow, Dissolve, CRT, Hologram, etc.) with multi-pass chaining via `postEffects`, plus custom shader support on both GPU backends: `ShaderEffect` for per-sprite fragment effects (GLSL and/or WGSL bodies) and complete custom mesh shader programs via `mesh.shader` (a dual-language `GLShader`: GLSL pair and/or WGSL module)
- Trail renderable for fading, tapering ribbons behind moving objects (speed lines, sword slashes, magic trails)
- System & Bitmap Text with built-in typewriter effect
- Video sprite playback
Expand Down Expand Up @@ -98,7 +98,7 @@ UI
Scenes
- Load a scene in one call with `level.load(name)` — 2D Tiled maps and 3D glTF scenes alike, auto-registered on preload
- [Tiled](https://www.mapeditor.org) map format [up to 1.12](https://doc.mapeditor.org/en/stable/reference/tmx-changelog/) built-in support for easy level design
- **GPU-accelerated tile rendering** for orthogonal maps under WebGL 2 — each layer draws as a single quad with no per-tile loop, ~5–8× faster than the legacy CPU renderer on dense maps. Honors animated tiles, flip bits, per-layer opacity/tint/blend, and oversized bottom-aligned tiles; falls back transparently to the CPU renderer on isometric/staggered/hexagonal layers or non-WebGL-2 contexts
- **GPU-accelerated tile rendering** for orthogonal maps on the GPU backends (WebGL 2 and WebGPU) — each layer draws as a single quad with no per-tile loop, ~5–8× faster than the legacy CPU renderer on dense maps. Honors animated tiles, flip bits, per-layer opacity/tint/blend, and oversized bottom-aligned tiles; falls back transparently to the CPU renderer on isometric/staggered/hexagonal layers or under the Canvas renderer
- Uncompressed and [compressed](https://github.com/melonjs/melonJS/tree/master/packages/tiled-inflate-plugin) Plain, Base64, CSV and JSON encoded XML tilemap loading
- Orthogonal, Isometric, Hexagonal (both normal and staggered) and Oblique maps
- Multiple layers with per-layer alpha, tinting and blend modes (multiple background/foreground, collision and Image layers)
Expand All @@ -116,7 +116,7 @@ Scenes
- Shape based Tile collision support
- glTF / GLB 3D scenes — load an authored 3D scene with `level.load(...)`, the same one call as a Tiled map
- The whole scene loads at once — meshes, materials, cameras and lights — viewed under a `Camera3d`
- Automatically lit by the scene's directional lights (the sun set up in the authoring tool)
- Automatically lit by the scene's authored lights the sun plus any point/spot lamps set up in the authoring tool
- Textured, solid-colored, and vertex-colored materials
- Node animation — walk/idle/sprint characters, spinning pickups, doors, lifts — played through the same `setCurrentAnimation` / `play` / `pause` / `stop` API as a 2D `Sprite`
- `.glb` and `.gltf` files, with embedded *or* external buffers & textures
Expand Down Expand Up @@ -179,6 +179,7 @@ Examples
* [SVG Shapes](https://melonjs.github.io/melonJS/examples/#/svg-shapes) ([source](https://github.com/melonjs/melonJS/tree/master/packages/examples/src/examples/svgShapes))
* [Graphics](https://melonjs.github.io/melonJS/examples/#/graphics) ([source](https://github.com/melonjs/melonJS/tree/master/packages/examples/src/examples/graphics))
* [Hello World](https://melonjs.github.io/melonJS/examples/#/hello-world) ([source](https://github.com/melonjs/melonJS/tree/master/packages/examples/src/examples/helloWorld))
* [Hello WebGPU](https://melonjs.github.io/melonJS/examples/#/webgpu) ([source](https://github.com/melonjs/melonJS/tree/master/packages/examples/src/examples/webgpu)) — the WebGPU backend in action, with the renderer negotiation surfaced on screen
* [Whac-A-Mole](https://melonjs.github.io/melonJS/examples/#/whac-a-mole) ([source](https://github.com/melonjs/melonJS/tree/master/packages/examples/src/examples/whac-a-mole))
* [Compressed Textures](https://melonjs.github.io/melonJS/examples/#/compressed-textures) ([source](https://github.com/melonjs/melonJS/tree/master/packages/examples/src/examples/compressedTextures))
* [Aquarium](https://melonjs.github.io/melonJS/examples/#/aquarium) ([source](https://github.com/melonjs/melonJS/tree/master/packages/examples/src/examples/aquarium)) — screen-space water refraction with `renderer.toFrameTexture()`: fish swim across a seabed, then a water surface captures the live frame on the GPU and re-samples it through a scrolling `NoiseTexture2d` flow map (the standard screen-texture / opaque-frame-copy pattern)
Expand Down
23 changes: 13 additions & 10 deletions packages/examples/src/examples/afterBurner/ExampleAfterBurner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,28 +54,31 @@ const createGame = async () => {
// StrictMode remount, the preload callback bails for the rest of
// the session, and the game never starts. Picks up cleanly once
// the utils.tsx remount path is fixed (separate review thread).
// AfterBurner requires WebGL — `renderer: video.WEBGL` throws (post
// #1479) when the browser/GPU can't provide a context (driver
// blocklisted, software fallback failing the perf-caveat check, etc.).
// Surface a clear browser-level message to the user instead of
// letting the React tree render a stuck blank canvas with an obscure
// error buried in the dev console.
// AfterBurner needs a GPU backend — `video.AUTO` negotiates WebGPU
// first, then WebGL 2, and only lands on Canvas when neither exists
// (driver blocklisted, software fallback failing the perf-caveat
// check, etc.). Canvas has no Camera3d/mesh path, so surface a clear
// browser-level message instead of letting the React tree render a
// stuck blank canvas with an obscure error buried in the dev console.
let app: Application;
try {
app = new Application(1024, 576, {
parent: "screen",
renderer: video.WEBGL,
renderer: video.AUTO,
scale: "auto",
cameraClass: Camera3d,
});
await app.init();
if (!app.renderer.supportsDepthBuffer) {
throw new Error("no GPU backend available (Canvas fallback)");
}
} catch (err) {
const reason = err instanceof Error ? err.message : String(err);
globalThis.alert(
"AfterBurner couldn't start: WebGL isn't available in this browser.\n\n" +
"AfterBurner couldn't start: no GPU rendering is available in this browser.\n\n" +
"This showcase uses Camera3d + 3D mesh rendering, which require a " +
"WebGL-capable browser/GPU. Try enabling hardware acceleration in " +
"your browser settings, or open this example in a different browser.\n\n" +
"WebGPU- or WebGL-capable browser/GPU. Try enabling hardware acceleration " +
"in your browser settings, or open this example in a different browser.\n\n" +
`Details: ${reason}`,
);
// Re-throw so the React example boundary doesn't think we
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,7 @@ const createGame = async () => {
// full-screen renderables cover it regardless of the container size)
scale: "auto",
// toFrameTexture + ShaderEffect are WebGL features
renderer: video.WEBGL,
renderer: video.AUTO,
antiAlias: true,
// render at sub-pixel positions so the slow-swimming fish glide smoothly
// instead of snapping pixel-to-pixel (default floors dx/dy to integers)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ const createGame = async () => {
try {
app = new Application(1024, 768, {
parent: "screen",
renderer: video.WEBGL,
renderer: video.AUTO,
scale: "auto",
cameraClass: Camera3dClass,
antiAlias: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ const createGame = async () => {
// to Camera2d via its own constructor regardless).
const app = new Application(1024, 768, {
parent: "screen",
renderer: video.WEBGL,
renderer: video.AUTO,
scale: "auto",
cameraClass: Camera3dClass,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ const createGame = async () => {
const app = new Application(800, 600, {
parent: "screen",
scaleMethod: "flex",
renderer: video.WEBGL,
renderer: video.AUTO,
});
await app.init();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ const createGame = async () => {
// canvas down to a horizontal banner.
parent: "screen",
scaleMethod: "flex",
renderer: video.CANVAS,
renderer: video.AUTO,
});
await app.init();
} catch {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ const createGame = async () => {
const app = new Application(1024, 768, {
parent: "screen",
scale: "auto",
renderer: video.CANVAS,
renderer: video.AUTO,
});
await app.init();
} catch {
Expand Down
2 changes: 1 addition & 1 deletion packages/examples/src/examples/gltf/ExampleGltf.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ const createGame = async () => {
try {
app = new Application(1024, 768, {
parent: "screen",
renderer: video.WEBGL, // Mesh rendering requires WebGL
renderer: video.AUTO,
scale: "auto",
cameraClass: Camera3dClass,
antiAlias: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ const createGame = async () => {
try {
app = new Application(1024, 768, {
parent: "screen",
renderer: video.WEBGL, // Mesh rendering requires WebGL
renderer: video.AUTO,
scale: "auto",
cameraClass: Camera3dClass,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ const createGame = async () => {
parent: "screen",
scale: "auto",
// Light2d normal-map lighting + toFrameTexture are WebGL features
renderer: video.WEBGL,
renderer: video.AUTO,
antiAlias: true,
subPixel: true,
});
Expand Down
21 changes: 12 additions & 9 deletions packages/examples/src/examples/mesh3d/ExampleMesh3d.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,25 +22,28 @@ import { createExampleComponent } from "../utils";
const base = `${import.meta.env.BASE_URL}assets/mesh3d/`;

const createGame = async () => {
// mesh3d uses `me.Mesh`, which requires WebGL. Switch to
// `renderer: video.WEBGL` so the engine throws (post #1479) when the
// browser/GPU can't provide a context, instead of silently falling
// back to Canvas and producing a broken scene with no signal.
// mesh3d uses `me.Mesh`, which needs a GPU backend. `video.AUTO`
// negotiates WebGPU first, then WebGL 2; only a browser with neither
// lands on Canvas — fail loudly there instead of rendering a broken
// scene with no signal.
let app: Application;
try {
app = new Application(1024, 768, {
parent: "screen",
renderer: video.WEBGL,
renderer: video.AUTO,
scale: "auto",
});
await app.init();
if (!app.renderer.supportsDepthBuffer) {
throw new Error("no GPU backend available (Canvas fallback)");
}
} catch (err) {
const reason = err instanceof Error ? err.message : String(err);
globalThis.alert(
"This example couldn't start: WebGL isn't available in this browser.\n\n" +
"The 3D mesh rendering used by this showcase requires a WebGL-capable " +
"browser/GPU. Try enabling hardware acceleration in your browser " +
"settings, or open this example in a different browser.\n\n" +
"This example couldn't start: no GPU rendering is available in this browser.\n\n" +
"The 3D mesh rendering used by this showcase requires a WebGPU- or " +
"WebGL-capable browser/GPU. Try enabling hardware acceleration in your " +
"browser settings, or open this example in a different browser.\n\n" +
`Details: ${reason}`,
);
throw err;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,27 +62,29 @@ const CRAFTS = [
// ─── entry point ──────────────────────────────────────────────────

const createGame = async () => {
// `renderer: video.WEBGL` throws (post #1479) when the browser/GPU
// can't provide a context. Surface a clear browser-level message
// instead of letting the React tree render a stuck blank canvas.
// Multi-material 3D meshes need a GPU backend for usable frame rates
// — Canvas would solid-fill per triangle in JS, correct but 10-50×
// slower than the GPU rasterizer. `video.AUTO` negotiates WebGPU
// first, then WebGL 2; fail loudly on the Canvas fallback instead of
// letting the React tree render a stuck blank canvas.
let app: Application;
try {
app = new Application(CANVAS_W, CANVAS_H, {
parent: "screen",
// Multi-material 3D meshes need the WebGL renderer for usable
// frame rates — Canvas would solid-fill per triangle in JS,
// correct but 10-50× slower than the GPU rasterizer.
renderer: video.WEBGL,
renderer: video.AUTO,
scale: "auto",
});
await app.init();
if (!app.renderer.supportsDepthBuffer) {
throw new Error("no GPU backend available (Canvas fallback)");
}
} catch (err) {
const reason = err instanceof Error ? err.message : String(err);
globalThis.alert(
"This example couldn't start: WebGL isn't available in this browser.\n\n" +
"The 3D mesh rendering used by this showcase requires a WebGL-capable " +
"browser/GPU. Try enabling hardware acceleration in your browser " +
"settings, or open this example in a different browser.\n\n" +
"This example couldn't start: no GPU rendering is available in this browser.\n\n" +
"The 3D mesh rendering used by this showcase requires a WebGPU- or " +
"WebGL-capable browser/GPU. Try enabling hardware acceleration in your " +
"browser settings, or open this example in a different browser.\n\n" +
`Details: ${reason}`,
);
throw err;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ const createGame = async () => {
try {
app = new Application(1024, 768, {
parent: "screen",
renderer: video.WEBGL, // Mesh rendering requires WebGL
renderer: video.AUTO,
scale: "auto",
cameraClass: Camera3dClass,
// Showcase the decoupled antiAlias / textureFilter combo:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ const createGame = async () => {
// Normal-map lighting needs the WebGL renderer's lit pipeline.
// Under `video.AUTO`, a Canvas fallback would render the orbs
// as flat sprites and emit a one-shot console warning.
renderer: video.WEBGL,
renderer: video.AUTO,
});
await app.init();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ const createGame = async () => {
parent: "screen",
scaleMethod: "flex",
// per-pixel normal-map lighting needs the WebGL renderer
renderer: video.WEBGL,
renderer: video.AUTO,
});
await app.init();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const createGame = async () => {
try {
const app = new Application(1024, 840, {
parent: "screen",
renderer: video.WEBGL,
renderer: video.AUTO,
blendMode: "normal",
});
await app.init();
Expand Down
Loading