Almost every game is more than just boxes and circles. Excalibur provides an asset loading feature that can help you preload things like JSON files, images, and sounds.

Creating an asset loader

When calling Engine.start, you can optionally pass an asset Loader. This loader will contain a reference to any "loadables" you want to load.

const loader = new ex.Loader([
  /* add Loadables here */

Loadables are different kinds of assets such as textures, sounds, and generic resources.

Anytime you call game.start(loader), the game will pause and the engine will load assets. This means that you do not have to load every asset at once! Instead you may want to call game.start(loader) initially with core assets and then again when initializing a Scene.

Using a web server

The asset loader only works with a web server since it loads assets with XHR. That means you cannot use the loader whenrunning an HTML file locally from the file-system (e.g. a file:// protocol URL will not work). The browser throws errors that will prevent you from loading assets.

The fastest way to serve a folder of files is by using the serve NPM package.

# Serve the current directory
npx serve .

# Serve a folder
npx serve ./dist

If you are developing a game using Excalibur with Webpack, Parcel, or another bundler, these typically already come with dev servers for running your game. See Excalibur project templates for templates you can start from that use these tools.

Relative vs. absolute paths

Given this directory structure:


And you serve from the root directory like this:

> cd root
> npx serve .

Now serving on http://localhost:3000/

The path to your assets doesn't matter as much because both absolute and relative paths will work:

  • /assets/textures/map.png => HTTP 200 OK
  • assets/textures/map.png => HTTP 200 OK

But if you are serving under a sub-directory, like http://localhost:3000/root/index.html then the format of your paths matter:

  • /assets/textures/map.png => HTTP 404 Not Found
  • assets/textures/map.png => HTTP 200 OK

The first path will fail to load as the absolute asset path would now be /root/assets and not /assets. Use a relative path to load assets relative to the HTML file serving your game.

Setting the base for a page

In your HTML file(s), to set the base for any absolute paths like the example above, you can use the base tag:

<!DOCTYPE html>
    <!-- Set the base for all absolute URLs -->
    <base href="/root" />
    <!-- The browser will now properly resolve /root/game.js -->
    <script src="/game.js"></script>

This can be accessed programmatically using document.baseUri to resolve absolute paths in JavaScript.

This is a good approach to use when hosting your game at a sub-directory, such as publishing to GitHub Pages.

Asset types

Images and textures

Textures are the raw images that back sprites. Excalibur supports loading most formats that browsers natively support like PNG, BMP, and JPEG. GIFs are even converted to Excalibur animations.

Pass an instance of Texture to a Loader to preload it. Once a texture is loaded, you can generate a sprite with it:

const txPlayer = new ex.Texture('/assets/tx/player.png')
const loader = new ex.Loader([txPlayer])
game.start(loader).then(function () {
  const player = new ex.Actor()


Excalibur supports audio assets like WAV and MP3. Any audio codec or container supported by browsers should be able to work with Excalibur. For the widest compatibility, typically we recommend MP3 as it combines quality and compression for the best results.

Pass an instance of Sound to a Loader to preload it. Once a sound is loaded, you can play it. You can pass an argument from 0.0 - 1.0 into play in order to play the sound at that volume.

// define multiple sources (such as mp3/wav/ogg) as a browser fallback
const sndPlayerDeath = new ex.Sound(
const loader = new ex.Loader(sndPlayerDeath)
game.start(loader).then(function () {

See the examples or API documentation for Sound for additional features available such as looping, volume setting, and more.

Generic resources

Resource is a generic Loadable like JSON files, compressed files, or binary files. It passes the raw data interpreted by browser based on the MIME type. See XHLHttpRequest.response for the different kinds of data to expect when loading. This response is passed to a Resource.processData method you need to implement:

const resLevel1 = new ex.Resource('/assets/levels/1.json', 'application/json')
const loader = new ex.Loader(resLevel1)
// attach a handler to process once loaded
resLevel1.processData = function (data) {
  // process JSON
  const json = JSON.parse(data)
  // create a new level (inherits Scene) with the JSON configuration
  const level = new Level(json)
  // add a new scene
  game.add(, level)

For a more complex example of using generic resources, see the Excalibur Tiled plug-in that loads Tiled map editor files.

Custom loadables

You can implement the Loadable interface to create your own custom loadables. One example of a custom loadable is the excalibur-tiled plugin which can load Tiled map editor files.

This is an advanced feature, as the Resource class already wraps logic around blob/plain data for usages like JSON, configuration, levels, etc through XHR (Ajax).

However, as long as you implement the facets of a loadable, you can create your own.