Skip to content

Jelly Jumper (Excalibur)

A small 2D platformer built with the unmodified Excalibur.js game engine and Tiled tilemaps. The same npm package runs in the browser and on GJS — Excalibur reaches for <canvas>, WebGL, XMLHttpRequest, DOMParser, ResizeObserver, and requestAnimationFrame, and every one of those calls is served by the corresponding @gjsify/* package on GJS.

PillarWhat Excalibur needs
@gjsify/webglWebGLBridgeGtk.GLArea (Excalibur’s WebGL renderer)
@gjsify/dom-elementsHTMLCanvasElement, ResizeObserver (the ancestor-walking one — see Bridges)
@gjsify/xmlhttprequestExcalibur’s asset loader (responseType: 'arraybuffer' + 'blob')
@gjsify/domparserexcalibur-tiled parses the .tmx XML map files
@gjsify/event-bridgeKeyboard input (jump / move / restart)
@gjsify/webaudioSound effects via Excalibur’s WebAudio backend (GStreamer pipeline on GJS)

This is the showcase that surfaced the ResizeObserver-on-bridge bug in v0.4.20 — DisplayMode.FillContainer mode observes canvas.parentElement, which only fires when the bridge’s GTK resize signal walks the polyfill DOM tree’s ancestors. The fix lives in @gjsify/dom-elements’s notify-resize and is what makes this showcase reflow correctly on window resize.

gjsify showcase excalibur-jelly-jumper

Opens a GTK4 window with the GLArea-backed canvas filling it. Arrow keys move, space jumps, R restarts. The Excalibur engine ticks at the GTK frame clock’s vsync.

import { mount } from '@gjsify/example-dom-excalibur-jelly-jumper/browser';
mount(document.getElementById('app')!);

The asset loader fetches tilemaps + spritesheets from @gjsify/example-dom-excalibur-jelly-jumper/assets/* (resolved via require.resolve in both targets — the browser bundles them via the website’s Vite config).

showcases/dom/excalibur-jelly-jumper/ — entry points: src/gjs/gjs.ts (Adw.Application + JellyJumperWindow), src/browser/browser.ts (mount module — Adwaita-styled shell + startGame(canvas)), src/browser/browser-main.ts (standalone fullscreen browser entry). Shared scene/actor code lives at the top level — src/game.ts, src/resources.ts, src/scenes/, src/actors/, src/components/, src/physics/, src/state/, src/ui/ — and runs unchanged in both targets.

Excalibur is the canonical real-world consumer of the full WebGL + Canvas + WebAudio + XHR + DOMParser stack. Every time we promote a @gjsify/* package from Partial → Full, this showcase is one of the first regression checks — if Excalibur’s “FillContainer” mode reflows correctly, if its asset loader resolves binary blobs without truncation, if its WebAudio mixer plays a multi-channel sound effect without artifacts, then the gjsify port of the underlying API is genuinely production-grade.