Skip to content

Rendering API

Olivier Biot edited this page Aug 5, 2026 · 12 revisions

The melonJS rendering API is designed to feel familiar to anyone who has used the HTML5 Canvas 2D API (CanvasRenderingContext2D). Whether you choose the Canvas, WebGL or WebGPU renderer, the API remains the same — write your game once, and it runs on every backend with zero code changes.

Quick Example

// This code works identically on Canvas and WebGL
renderer.save();
renderer.translate(100, 50);
renderer.rotate(Math.PI / 4);
renderer.setColor("#FF0000");
renderer.fillRect(0, 0, 64, 64);
renderer.restore();

If you've used ctx.save(), ctx.translate(), ctx.fillRect() on a Canvas 2D context, you already know the melonJS rendering API.

Live Examples

See these features in action:

  • Graphics — shapes, paths, bezier curves, dashed lines, masks, transforms
  • Gradients — linear/radial gradients on rects, ellipses, polygons, rounded rects
  • Blend Modes — all supported composite operations

API Reference Table

The table below maps every melonJS renderer method to its Canvas 2D equivalent.

State Management

melonJS Canvas 2D Equivalent Description
save() ctx.save() Push the current renderer state onto a stack
restore() ctx.restore() Pop and restore the most recently saved state
reset() Reset the entire renderer context to its default state
clear() Prepare the framebuffer for a new frame
flush() Flush the render pipeline (WebGL batches; no-op on Canvas)

Transforms

melonJS Canvas 2D Equivalent Description
translate(x, y) ctx.translate(x, y) Add a translation transformation
rotate(angle) ctx.rotate(angle) Add a rotation (radians) to the transformation matrix
scale(x, y) ctx.scale(x, y) Add a scaling transformation
transform(a, b, c, d, e, f) ctx.transform(a, b, c, d, e, f) Multiply the current matrix by the given one
setTransform(a, b, c, d, e, f) ctx.setTransform(a, b, c, d, e, f) Reset the matrix then apply the given one
resetTransform() ctx.setTransform(1,0,0,1,0,0) Reset the transformation matrix to identity
setProjection(matrix) Set the projection matrix (orthographic)

Color, Gradients & Alpha

melonJS Canvas 2D Equivalent Description
setColor(color) ctx.fillStyle / ctx.strokeStyle Set the current fill and stroke color (accepts Color, string, or Gradient)
getColor() Get the current fill and stroke color
createLinearGradient(x0, y0, x1, y1) ctx.createLinearGradient(x0, y0, x1, y1) Create a linear gradient fill
createRadialGradient(x0, y0, r0, x1, y1, r1) ctx.createRadialGradient(x0, y0, r0, x1, y1, r1) Create a radial gradient fill
gradient.addColorStop(offset, color) gradient.addColorStop(offset, color) Add a color stop to a gradient
setGlobalAlpha(alpha) ctx.globalAlpha = alpha Set the global alpha (opacity)
getGlobalAlpha() ctx.globalAlpha Get the current global alpha
setTint(tint, alpha) Set a coloring tint for sprite rendering
clearTint() Clear the current rendering tint
setBlendMode(mode) ctx.globalCompositeOperation Set the blend/composite mode
getBlendMode() ctx.globalCompositeOperation Get the current blend mode

Gradient example:

const gradient = renderer.createLinearGradient(0, 0, 200, 0);
gradient.addColorStop(0, "#00FF00");
gradient.addColorStop(0.5, "#FFFF00");
gradient.addColorStop(1, "#FF0000");
renderer.setColor(gradient);
renderer.fillRect(10, 10, 200, 24);

Drawing — Rectangles

melonJS Canvas 2D Equivalent Description
fillRect(x, y, w, h) ctx.fillRect(x, y, w, h) Fill a rectangle
strokeRect(x, y, w, h) ctx.strokeRect(x, y, w, h) Stroke a rectangle outline
clearRect(x, y, w, h) ctx.clearRect(x, y, w, h) Erase pixels in a rectangular area
clearColor(color, opaque) Clear the framebuffer with a solid color
fillRoundRect(x, y, w, h, r) ctx.roundRect() + ctx.fill() Fill a rounded rectangle
strokeRoundRect(x, y, w, h, r) ctx.roundRect() + ctx.stroke() Stroke a rounded rectangle outline

Drawing — Paths

melonJS Canvas 2D Equivalent Description
beginPath() ctx.beginPath() Start a new path
closePath() ctx.closePath() Close the current sub-path
moveTo(x, y) ctx.moveTo(x, y) Move to a point without drawing
lineTo(x, y) ctx.lineTo(x, y) Draw a straight line to a point
quadraticCurveTo(cpx, cpy, x, y) ctx.quadraticCurveTo(cpx, cpy, x, y) Add a quadratic Bezier curve to the path
bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) Add a cubic Bezier curve to the path
arcTo(x1, y1, x2, y2, radius) ctx.arcTo(x1, y1, x2, y2, radius) Add a circular arc using control points and radius
rect(x, y, w, h) ctx.rect(x, y, w, h) Add a rectangle to the current path
roundRect(x, y, w, h, radii) ctx.roundRect(x, y, w, h, radii) Add a rounded rectangle to the current path
stroke(shape) ctx.stroke() Stroke the current path or a given shape
fill(shape) ctx.fill() Fill the current path or a given shape

Bezier curve example:

renderer.beginPath();
renderer.setColor("#10b981");
renderer.moveTo(0, 100);
renderer.bezierCurveTo(100, 0, 200, 200, 300, 100);
renderer.stroke();

Drawing — Shapes

melonJS Canvas 2D Equivalent Description
strokeArc(x, y, r, start, end) ctx.arc() + ctx.stroke() Stroke an arc
fillArc(x, y, r, start, end) ctx.arc() + ctx.fill() Fill an arc
strokeEllipse(x, y, w, h) ctx.ellipse() + ctx.stroke() Stroke an ellipse
fillEllipse(x, y, w, h) ctx.ellipse() + ctx.fill() Fill an ellipse
strokeLine(x1, y1, x2, y2) ctx.moveTo() + ctx.lineTo() + ctx.stroke() Stroke a line between two points
fillLine(x1, y1, x2, y2) ctx.moveTo() + ctx.lineTo() + ctx.fill() Fill a line between two points
strokePolygon(poly) ctx.lineTo() loop + ctx.stroke() Stroke a polygon
fillPolygon(poly) ctx.lineTo() loop + ctx.fill() Fill a polygon
strokePoint(x, y) Stroke a single point
fillPoint(x, y) Fill a single point

Images & Patterns

melonJS Canvas 2D Equivalent Description
drawImage(image, ...) ctx.drawImage(image, ...) Draw an image (supports all 3/5/9 argument forms)
createPattern(image, repeat) ctx.createPattern(image, repeat) Create a repeating image pattern
drawPattern(pattern, x, y, w, h) ctx.fillStyle = pattern + ctx.fillRect() Draw a pattern within a rectangle

Clipping & Masking

melonJS Canvas 2D Equivalent Description
clipRect(x, y, w, h) ctx.rect() + ctx.clip() Clip rendering to a rectangular region
setMask(mask, invert) ctx.clip() / gl.stencilFunc() Apply an arbitrary shape as a rendering mask
clearMask() Remove the current rendering mask

Line Style

melonJS Canvas 2D Equivalent Description
setLineDash(segments) ctx.setLineDash(segments) Set the dash pattern for stroke operations
getLineDash() ctx.getLineDash() Get the current dash pattern
lineWidth ctx.lineWidth Line thickness (property, not a method)

Dashed line example:

renderer.setLineDash([10, 5]);
renderer.strokeLine(0, 50, 300, 50);
renderer.setLineDash([]); // back to solid

Pixel Smoothing

melonJS Canvas 2D Equivalent Description
setAntiAlias(enable) ctx.imageSmoothingEnabled Enable or disable image smoothing/anti-aliasing

Canvas Access

melonJS Canvas 2D Equivalent Description
getCanvas() canvas Return the underlying canvas element
getContext() canvas.getContext('2d') Return the underlying rendering context
resize(w, h) Setting canvas.width / canvas.height Resize the rendering canvas
overlaps(bounds) Check if a bounding box overlaps the visible area
toBlob(type, quality) canvas.toBlob() Export the current frame as a Blob
toDataURL(type, quality) canvas.toDataURL() Export the current frame as a data URL
toImageBitmap(type, quality) createImageBitmap(canvas) Export the current frame as an ImageBitmap

GPU-Backend-Specific

These methods are available on every renderer but only do meaningful work on the GPU backends (WebGL and WebGPU). On Canvas they are safe to call but have no effect or return empty/default values:

melonJS Description
addBatcher(batcher, name) Add a custom batcher for specialized rendering (extend WebGLBatcher / WebGPUBatcher to match the active backend)
setBatcher(name, shader) Switch the active rendering batcher
setViewport(x, y, w, h) Set the viewport rectangle (split-screen cameras)
getSupportedCompressedTextureFormats() List supported GPU compressed texture formats (returns empty on Canvas)

Key Differences from Canvas 2D

While the API mirrors Canvas 2D closely, there are a few design differences:

  1. Unified color setter — melonJS uses setColor(color) to set both fillStyle and strokeStyle at once, rather than having two separate properties.

  2. Convenience shape methods — Instead of building paths manually, melonJS provides direct strokeRect, fillEllipse, strokePolygon, etc. methods that accept melonJS shape objects (Rect, Ellipse, Polygon).

  3. TintingsetTint() / clearTint() provide sprite-level color tinting with no Canvas 2D equivalent.

  4. Automatic batching — The GPU renderers automatically batch draw calls. Call flush() explicitly only when you need to force a batch submission (e.g. before reading pixels).

  5. Blend modes — Every renderer supports the same blend mode names (normal, multiply, screen, additive, darken, lighten, none, etc.), abstracting away the difference between globalCompositeOperation (Canvas), GL blend functions (WebGL), and pipeline blend states (WebGPU).

Choosing a Renderer

import { Application, video } from "melonjs";

// Auto-negotiate the best backend: WebGPU → WebGL 2 → Canvas
const app = new Application(800, 600, { renderer: video.AUTO });

// Force one backend — app.init() rejects if it's unavailable (no substitution)
const app = new Application(800, 600, { renderer: video.WEBGPU });
const app = new Application(800, 600, { renderer: video.WEBGL });

// Force Canvas
const app = new Application(800, 600, { renderer: video.CANVAS });

The renderer is accessed via app.renderer and provides the full API listed above regardless of which backend is active. Code that needs to know what it got should read the capability flags rather than sniff types:

Flag Canvas WebGL WebGPU
renderer.type "CANVAS" "WebGL2" "WebGPU"
renderer.shaderLanguage null "glsl" "wgsl"
renderer.supportsDepthBuffer (Camera3d / 3D meshes) false true true
renderer.supportsRetainedMesh (GPU-resident mesh geometry) false true true
renderer.supportsShaderTileLayers (GPU tilemap path) false true true

Path2D

melonJS uses an internal Path2D class to build and tessellate 2D paths for the WebGL renderer. On Canvas, path methods (moveTo, lineTo, bezierCurveTo, etc.) are direct pass-throughs to the native context. On WebGL, they are routed through Path2D which tessellates curves into line segments for the primitive batcher.

Path2D also supports SVG path strings for defining shapes:

import Path2D from "melonjs";

// a heart shape using arcs and quadratic curves
const heart = new Path2D(
    "M 10 30 A 20 20 0 0 1 50 30 A 20 20 0 0 1 90 30 Q 90 60 50 90 Q 10 60 10 30 Z"
);

Supported SVG commands: M (moveTo), L (lineTo), H (horizontal line), V (vertical line), Q (quadratic Bezier), C (cubic Bezier), A (arc), Z (close path).

Custom Drawing in a Renderable

To draw custom graphics, override the draw method of a Renderable:

import { Renderable } from "melonjs";

class MyGraphics extends Renderable {
    constructor() {
        super(0, 0, 200, 200);
        this.anchorPoint.set(0, 0);
    }

    draw(renderer) {
        // sky gradient
        const sky = renderer.createLinearGradient(0, 0, 0, 100);
        sky.addColorStop(0, "#1a1a6e");
        sky.addColorStop(1, "#FF6B35");
        renderer.setColor(sky);
        renderer.fillRect(0, 0, 200, 100);

        // dashed horizon line
        renderer.setLineDash([8, 4]);
        renderer.setColor("#FFFFFF");
        renderer.strokeLine(0, 100, 200, 100);
        renderer.setLineDash([]);

        // bezier curve hill
        renderer.beginPath();
        renderer.setColor("#2d5016");
        renderer.moveTo(0, 150);
        renderer.bezierCurveTo(50, 80, 150, 80, 200, 150);
        renderer.lineTo(200, 200);
        renderer.lineTo(0, 200);
        renderer.fill();
    }
}

3D Mesh Rendering

melonJS supports rendering textured 3D triangle meshes on both WebGL and Canvas renderers. Meshes can be loaded from Wavefront OBJ files or created from raw geometry data.

Live Example

  • 3D Mesh — rotating 3D objects (cube, sphere, teapot) with perspective projection over a 2D background

Loading an OBJ Model

import { loader, Mesh } from "melonjs";

// preload the OBJ model and its texture
loader.preload([
    { name: "myModel", type: "obj", src: "models/cube.obj" },
    { name: "myModel", type: "image", src: "models/texture.png" },
], () => {
    const mesh = new Mesh(400, 300, {
        model: "myModel",
        texture: "myModel",
        width: 200,
        height: 200,
    });
    app.world.addChild(mesh);
});

Creating from Raw Geometry

const mesh = new Mesh(400, 300, {
    vertices: new Float32Array([...]),  // x, y, z triplets
    uvs: new Float32Array([...]),       // u, v pairs
    indices: new Uint16Array([...]),    // triangle indices
    texture: myTextureAtlas,
    width: 200,
    height: 200,
});

3D Transforms

The Mesh class extends Renderable and supports all standard transform methods, plus 3D rotation and translation:

import { Vector3d } from "melonjs";

// 2D rotation (Z axis, same as Sprite)
mesh.rotate(Math.PI / 4);

// 3D rotation around any axis
mesh.rotate(Math.PI / 4, new Vector3d(0, 1, 0)); // Y axis
mesh.rotate(0.5, new Vector3d(1, 0, 0));          // X axis

// scale (z defaults to 1, preserving depth)
mesh.scale(2, 2);

// translate in 3D
mesh.translate(10, 20, 5);

// all Renderable properties work
mesh.setOpacity(0.8);
mesh.tint.setColor(128, 196, 255);
mesh.flipX(true);

Perspective Projection

Each Mesh has a built-in projectionMatrix that defaults to a perspective projection. Objects farther from the camera appear smaller:

// customize the projection (optional — default works for most cases)
mesh.projectionMatrix.identity();
mesh.projectionMatrix.perspective(
    Math.PI / 4,  // 45° field of view
    1,             // aspect ratio
    0.1,           // near plane
    10,            // far plane
);
mesh.projectionMatrix.translate(0, 0, -3); // camera distance

Backface Culling

Enabled by default. Triangles facing away from the camera are not rendered:

mesh.cullBackFaces = true;  // default
mesh.cullBackFaces = false; // render both sides

Renderer drawMesh API

The low-level drawMesh(mesh, modelMatrix?) method is available on every renderer:

melonJS Description
drawMesh(mesh) Draw a textured triangle mesh from CPU-projected vertices (the 2D-camera path)
drawMesh(mesh, modelMatrix) GPU backends: draw the mesh from retained GPU-resident geometry, placed by the matrix — redrawing never re-uploads vertices (renderer.supportsRetainedMesh)

GPU backends (WebGL / WebGPU): Hardware depth buffer for correct Z-ordering, GPU backface culling, and batched indexed drawing. Large CPU-projected meshes are automatically chunked to fit buffer limits; retained meshes draw whole in one call. Mesh.draw picks the right form automatically — see Working in 3D → Retained rendering.

Canvas: Uses painter's algorithm (back-to-front triangle sorting), per-triangle affine texture mapping with clip paths, and winding-order backface culling. Best suited for convex shapes; concave/self-overlapping geometry may have depth artifacts.

OBJ Format Support

The OBJ parser supports:

  • v — vertex positions (x, y, z)
  • vt — texture coordinates (u, v)
  • f — faces in v, v/vt, or v/vt/vn format
  • Quad and n-gon triangulation (fan from first vertex)
  • Automatic CW → CCW winding correction via signed volume test

Normals (vn) are parsed and used by the lighting path (mesh.lit + Light3d); when absent they are synthesized per face. The mtllib reference is extracted and stored with the parsed data.

MTL Material Support

melonJS supports Wavefront MTL material files for automatic texture and color assignment:

// preload OBJ + MTL + texture
loader.preload([
    { name: "fox", type: "obj", src: "models/fox.obj" },
    { name: "fox", type: "mtl", src: "models/fox.mtl" },
    { name: "colormap", type: "image", src: "models/colormap.png" },
], () => {
    // texture, tint, and opacity auto-resolved from the MTL file
    const mesh = new me.Mesh(400, 300, {
        model: "fox",
        material: "fox",
        texture: "colormap",
        width: 200,
        height: 200,
    });
});

Supported MTL properties:

  • newmtl — material name
  • Kd — diffuse color (baked as a per-vertex tint, per usemtl group)
  • Ke — emissive color (applied as Mesh.emissive — self-illumination)
  • d / Tr — opacity / transparency
  • map_Kd — diffuse texture path (auto-resolved relative to MTL file)
  • Multiple materials per mesh (usemtl groups) — each group's Kd is baked into a per-vertex color buffer, so multi-material models cost no extra draw calls (see the Multi-Material Mesh example)

Limitations:

  • Only one texture per mesh (map_Kd) is supported — per-group colors, not per-group textures
  • Specular maps (map_Ks), normal maps (map_bump), and other texture maps are not supported
  • Ka (ambient), Ns (specular exponent), and illum (illumination model) are parsed but ignored

Unsupported properties produce console warnings when encountered.

Live Examples

  • 3D Mesh — rotating textured 3D objects (cube, sphere, teapot) loaded from OBJ files
  • 3D Material — Kenney cube pet models with MTL material support

Clone this wiki locally