Skip to content

Jelly Jumper (Excalibur)

A 2D platformer built with Excalibur.js 0.32 and Tiled tilemaps. The engine comes straight from npm with no fork and no patch.

A game engine written for the browser, reaching for WebGL2, <canvas>, XMLHttpRequest, DOMParser, ResizeObserver, requestAnimationFrame and WebAudio, runs inside a native Adwaita window. If gjsify can carry Excalibur, it can carry the npm library you had in mind.

Based on sample-jelly-jumper by the Excalibur team.

It declares all four runtimes, so pick the one you have:

Terminal
gjsify showcase excalibur-jelly-jumper --runtime gjs
gjsify showcase excalibur-jelly-jumper --runtime node
gjsify showcase excalibur-jelly-jumper --runtime bun
gjsify showcase excalibur-jelly-jumper --runtime deno

A GTK 4 window opens with the game filling it and pause and mute buttons in the header bar. Same window and same game on all four.

ActionKeyboardGamepad
MoveArrow keysD-pad
JumpAFace button 1 (A on an Xbox pad)
RunSFace button 3 (X on an Xbox pad)
Performance overlayF1

The engine renders through WebGL2 on Gtk.GLArea. If no GL2 context is available it falls back to Canvas 2D over Cairo, the same fallback the browser build takes.

--runtime gjs runs the --app gjs bundle against GJS’s native gi://. The other three share one --app node bundle, where gi:// goes through the node-gi bridge. Leave the flag off and Choose the runtime says what the CLI picks.

TypeScript
import { mount } from '@gjsify/example-dom-excalibur-jelly-jumper/browser';
const handle = mount(document.getElementById('app')!, {
assetBase: '/demos/jelly-jumper/', // where the tilemaps and spritesheets live
startMuted: true,
});
handle.unmute();
handle.pause();

assetBase is what makes one resource table serve both targets. The game’s /res/... paths are rewritten against it at load time. The handle also exposes resume(), mute(), isPaused and isMuted.

Tilemaps and spritesheets ship with the package under @gjsify/example-dom-excalibur-jelly-jumper/assets/*.

PackageWhat Excalibur needs it for
@gjsify/webglWebGLBridge on Gtk.GLArea, Excalibur’s WebGL2 renderer
@gjsify/canvas2dThe Cairo fallback renderer when GL2 is unavailable
@gjsify/dom-elementsHTMLCanvasElement and the ResizeObserver that DisplayMode.FitContainerAndFill watches
@gjsify/xmlhttprequest and @gjsify/fetchThe asset loader, including arraybuffer and blob responses
@gjsify/domparser@excaliburjs/plugin-tiled parsing .tmx map files
@gjsify/event-bridgeKeyboard and gamepad input
@gjsify/webaudioSound effects, played through GStreamer in the GTK build

showcases/dom/excalibur-jelly-jumper/

Every target enters the same startGame(canvas, options) in src/game.ts. The scenes, actors, physics, components, state and UI all sit at the top level of src/ and never mention a platform. Only the shell differs. src/gjs/gjs.ts builds the Adw.Application and its window, src/browser/browser.ts exports mount(), and src/browser/browser-main.ts runs it full-page.

Per-target tuning goes through options on that one function (pixelRatio, fixedUpdateFps, enablePerf) rather than through forked code.

  • Bridge widgets covers the WebGLBridge lifecycle this game sits on, including how resizes reach a ResizeObserver.
  • canvas2d-fireworks is the smaller demo on the same bridges.
  • pixel-rpg/map-editor is a downstream project built on the same Excalibur and Tiled stack.