Why an asset manager matters

HTML5 games run in the browser, which means assets like images and audio are fetched over HTTP instead of read from a local disk. Network requests are asynchronous and their completion time is unpredictable. The naive approach is to chain onload handlers on individual Image objects, but that breaks down quickly once you have dozens of assets to coordinate.

An asset manager solves this by centralizing the logic for queuing, downloading, and tracking assets, and notifying the game when everything is ready.

Design goals

Our asset manager needs to do the following:

  • Queue downloads without starting them.
  • Start all queued downloads.
  • Track successes and failures.
  • Signal when all downloads have finished.
  • Provide easy access to downloaded assets.

Queue and download

Queuing lets you declare the assets you need ahead of time, which makes it possible to define level assets in a configuration file before fetching any of them. The manager exposes a queueDownload() method to add assets, storing them in an internal array.

Once everything is queued, calling downloadAll() iterates through that queue. For each item it creates an Image object, attaches listeners for load and error events, and assigns the image's src, which triggers the actual download.

Tracking progress

To know when all assets are finished, the manager counts successes and failures separately. Counters are incremented inside the respective event listeners, so a failed download doesn't leave the game hanging forever.

The manager also exposes an isDone() method that compares the sum of successful and failed downloads against the total size of the download queue. Both event handlers check this flag after incrementing their counters.

When the queue is exhausted and every asset has either loaded or errored, the manager invokes a callback that was passed to downloadAll(). This lets the game start rendering without polling for asset status.

Retrieving assets

After the manager has finished its work, the game needs to fetch the loaded images. The manager caches each downloaded asset in an internal object keyed by the path used in queueDownload(). The getAsset() method returns the cached object by that same key.

Edge case: empty queue

There is one subtle bug in the design described so far: isDone() is only checked within event handlers. If the queue is empty, no load or error event ever fires, so the callback has no way to run. The fix is to check the queue size at the start of downloadAll() and invoke the callback immediately if nothing was queued.

Using the manager

Basic usage follows this pattern:

  1. Create a new asset manager instance.
  2. Queue up the assets the game needs.
  3. Call downloadAll() with a callback.
  4. Use getAsset() inside the callback to retrieve loaded images.

What’s missing

This implementation is meant as a starting point, not a complete solution. Larger games typically require error reporting that identifies the failed asset, progress callbacks during download, or support for loading from new storage APIs. All of those can be layered onto this foundation.

The full source is available under the Apache License in the Bad Aliens repository, and the game it powers runs in any modern browser with Canvas support.