Build & Bundlers

To get started, first install Excalibur through npm (TypeScript typings are best supported in npm):

npm install excalibur

TypeScript Configuration

In a TypeScript project, you can reference Excalibur with the ES6 import style syntax:

// Excalibur is loaded into the ex global namespace
import * as ex from 'excalibur'

or

// Excalibur is loaded into the ex global namespace
import { Actor, Engine } from 'excalibur'

TSConfig

We have a base recommended tsconfig.json that the TypeScript compiler uses as configuration. In this example we assume all the source is in a ./src/ directory.

{
  "include": ["src/**/*"],
  "compilerOptions": {
    /* Basic Options */
    "target": "es2015" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */,
    "module": "es2015" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */,
    /* Specify library files to be included in the compilation. */
    "lib": [
      "dom",
      "es5",
      "es2015.Proxy",
      "es2015.promise",
      "es2015.collection",
      "es2015.iterable",
      "es2015"
    ],

    /* Strict Type-Checking Options */
    "strict": true /* Enable all strict type-checking options. */,

    /* Module Resolution Options */
    "moduleResolution": "node" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */,
    "baseUrl": "./src" /* Base directory to resolve non-absolute module names. */,
    "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */,

    /* Advanced Options */
    "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
  }
}

In a module loader system, such as Webpack or Parcel, it will automatically bundle Excalibur. See the webpack example repo or parcel repo

To support tree-shaking, you should use named imports:

import { Actor } from 'excalibur'

Excalibur doesn't do the best optimization to support tree-shaking--likely you'll end up importing everything at the moment but this is slowly getting better.

Parcel

Parcel is by far the easiest way to get a bundler up and running with excalibur. We recommend looking at the template.

Adding an import "regenerator-runtime/runtime"; in your entry file is needed to force parcel to understand async/await inside excalibur

Using npm to install parcel, excalibur, and typescript

npm install parcel-bundler excalibur typescript

Configure you tsconfig.json

tsc --init

Build your game script

// ./src/index.ts
const game = new Engine({
  width: 600,
  height: 400,
})

game.start()

Include the typescript file in your html

<html>
  <head></head>
  <body>
    <script src="./src/index.ts" />
  </body>
</html>

Build and run with parcel!

parcel index.html --no-autoinstall

Webpack

Webpack is the battleship solution, and if you need a lot of control over your build process, this might be the way to go.

Configuring webpack is not for the faint of heart, we recommend thoroughly reading webpack's documentation on how to understand and configure webpack.

Our template is a good starting point.

Deno

Deno is a runtime for JavaScript and TypeScript and much more than a bundler, but we put this section here since you'll be doing a lot of bundling :).

For Excalibur to work in a Deno environment, use a Content Delivery Network for making Node.js packages compatible with Deno: two popular ones are esm.sh and skypack.dev.

To keep it simple, we use esm.sh in the following examples, but feel free to make your own choice - at the time of writing the procedure for esm.sh and skypack.dev is nearly identical (for a difference, see the section Types and IntelliSense).

Importing within an HTML file

Sometimes, you just want to start hacking. You can do so by using Deno to get a bundled version of Excalibur and directly applying the module to your HTML.

Either esm.sh or skypack.dev should generate a bundle that works:

deno bundle https://esm.sh/excalibur excalibur.bundle.js

Learn more about the deno bundle command in the official docs.

Using as a native module works in Chrome without any extras:

<!-- index.html -->

<script type="module">
  import ex from './excalibur.bundle.js'

  const game = new ex.Engine()
  game.start()
</script>

The drawback to this method is that it doesn't give you type annotations in your IDE, like the following method.

Learn more about the type="module" attribute on MDN.

Importing into a JavaScript or TypeScript File

We have tested the following examples with version 0.26.0-alpha.264 of the excalibur package. We recommend you run at least that version for this to work. You can get the latest excalibur version on npm.

More likely, you will want to import Excalibur into a JavaScript or TypeScript file.

Since it goes very well with Deno, we use TypeScript in the following examples. We will have to set up our TypeScript compiler and pick our import syntax before we can make our bundle.

A custom tsconfig.json has to be used with strict turned off and a few DOM libraries added:

// tsconfig.json

{
  "compilerOptions": {
    "strict": false,
    "lib": ["dom", "dom.iterable", "dom.asynciterable", "Deno.ns"]
  }
}

Then Excalibur can be imported. This can be done in two ways:

Recommended: Named Import

// index.ts

import { Engine } from 'https://esm.sh/[email protected]'

const game = new Engine()
game.start()

Alternative: Namespace Import

// index.ts

import * as ex from 'https://esm.sh/[email protected]'

const game = new ex.Engine()
game.start()
Aside - Why do we recommend named imports over namespace imports?

There are two main reasons:

Tree shaking means dropping unused code, reducing the size of your bundle so that your app can load faster for the end-user. This can be done by build tools such as Rollup and Webpack.

Both approaches work. you might have your reasons for using namespace imports. We encourage you to do your own research!

The official MDN documentation is a great place to start learning about modules and imports.

Bundling and Applying

Set up your tsconfig.json and your index.ts as described in [the section above]((#importing-into-a-javascript-or-typescript-file). Then Deno should successfully bundle:

deno bundle index.ts game.bundle.js --config tsconfig.json

Learn more about the deno bundle command in the official Deno docs.

And you can apply it in your index.html with a regular script tag:

<script src="game.bundle.js"></script>

Types and IntelliSense

Screenshot demonstrating IntelliSense

To enable type annotations and IntelliSense in a Deno environment, you might need to install a special extension in your IDE, like this one for VSCode: Deno extension by denoland.

Make sure to read the instructions of the extension. you might also need to install the Deno CLI and later initialize the extension in your IDE workspace.

It might also take a few seconds until the IntelliSense starts working. Deno is resolving the import URLs and downloading the necessary files in the background.

If you're using skypack.dev, you might also need to add the ?dts query parameter to the package name. your import should read something like this:

import { Engine } from "https://cdn.skypack.dev/excalibur?dts

Or, if you want to use a specific version:

import { Engine } from "https://cdn.skypack.dev/excalibur@0.26.0-alpha.264?dts

Read more about TypeScript declarations in the official skypack.dev docs.