Why Graphics?

The Excalibur graphics context is responsible for displaying images and graphics to the screen.

The ExcaliburGraphicsContext provides an abstraction over the underyling drawing implementation.

This graphics context does a great deal to reduce the complexity of the drawing stack for both developers using Excalibur and the maintainers.

Excalibur has a graphics system that has been developed to hide the underyling implementation. This allows optimizations like webgl batch rendering and shader programs to increase the speed and power of our drawings.

WebGL vs Canvas support

The WebGL based implmentation of ExcaliburGraphicsContext is on by default, this implements an automatic 2d batch renderer that dramatically improves draw performance.

If you have need to switch to 2D canvas based rendering turn on the flag before engine construction.

const game = new ex.Engine(...);

How to use the Graphics Component

  1. Graphics using images (and any Resource) must be loaded before use
  2. Graphics like Sprites are like a window into an image
  3. Graphics like Canvas produce internal bitmap's which are large in memory and should be used sparingly or cached
  4. Graphics can be added to the GraphicsComponent on an Actor or Entity
  5. Direct access to the ExcaliburGraphicsContext

For most games, you will be using the graphics component off of Actors or plain Entities.

const image = new ex.ImageSource('./path/to/my/image.png')
await game.start()

const actor = new Actor({
  x: 100,
  y: 100,

Excalibur Graphics Context

ExcaliburGraphicsContext is the abstraction over the underlying drawing mechanism and only knows how to draw ex.Graphic objects.


Loading images from files (png, jpeg, etc), in some engines/frameworks is known as a Texture

ImageSource represents the source bitmap and contains the logic for loading that image from a file. You can thing of this as the file representation in Excalibur for the image.

These images can be presented to the loader at the start of an Excalibur game

const game = new ex.Engine({ width: 800, height: 600 })

const image = new ex.ImageSource('./img/myimg.png')

const loader = new ex.Loader()

game.start(loader).then(() => {
  // resources like ImageSource loaded before game started

Or they can be loaded out of band Note: it is important to check that a ImageSource has been loaded before using it at runtime to avoid errors and visual bugs if your game is loading images this way.

const image = new ex.ImageSource('./img/myimg.png')

image.load().then(() => {
  // image loaded
  // good for use in sprites inside this function

if (image.isLoaded()) {
  // is good for use in sprites


There is a new component, ex.GraphicsComponent to work with these graphics with ex.Actor's and other ex.Entity's

The ex.GraphicsComponent alows users to manage graphics with Actors and Entities in an easy way

Adding/Showing graphics

The graphics component allows developers to save named graphics to avoid passing around graphic object references if desired. These can be used to show specific graphics.'jump', jumpAnimation)'jump') // display the graphic
// equivalent to // display the graphic // hide the graphic

If no name is specified when added to the graphics component it is considered the 'default' graphic and is shown automatically.

// graphic considered 'default' and displayed automatically


The components adds a why multiple graphics on top or behind each other for a certain actor or entity.

Layers can be ordered numerically, larger negative layers behind, and positive layers in front.{ name: 'background', order: -1 }){ name: 'foreground', order: 1 })'background').show(myBackground)'foreground').show(myForeground)

// no longer display the background'background').hide()

There is always a layer named 'default' at order: 0
// is equivalent to'default').show(myAnimation)

Component Specific Overrides

  • visible: boolean

    • Shows or hides the all the graphics for this component
  • opacity: number

    • Applies an opacity to all the graphics shown for this component
  • offset: Vector

    • Offset in pixels to shift the graphics for this component
  • anchor: Vector

    • Anchor to apply to all drawings in this component if set, if null the drawing's anchor is respected.


Excalibur ex.Graphics break into 2 main categories, Sprite based and Raster based. That is to say Raster and non-Raster graphics.

Graphics have a bunch of useful feature that work for ALL types of graphics

  • clone(): Graphic

    • Returns a new deep copy of this graphic
  • width: number

    • Gets or sets the width of the graphic
  • height: number

    • Gets or sets the height of the graphic
  • flipHorizontal: boolean

    • Gets or sets the flipHorizontal, which will flip the graphic horizontally (across the y axis)
  • flipVertical: boolean

    • Gets or sets the flipVertical, which will flip the graphic vertically (across the x axis)
  • rotation: number

    • Gets or sets the rotation of the graphic (in radians)
  • opacity: number

    • Gets or sets the opacity of the graphic, 0 is transparent, 1 is solid (opaque).
  • scale: Vector

    • Gets or sets the scale of the graphic, this effects the width and height of the graphics
  • origin: Vector

    • Gets or sets the origin of the graphic, if not set the center of the graphic is the origin


For drawing hooks the ExcaliburGraphicsContext is replacing the browser CanvasRenderingContext2D. But if you need to do some custom drawing using the CanvasRenderingContext2D the new Canvas graphic has your back.

const canvas = new ex.Canvas({
    cache: true,  // If true draw once until flagged dirty again, otherwise draw to Canvas every frame
    draw: (ctx) => {
        console.log('With cache=true I draw once');
        ctx.fillStyle = 'red';
        ctx.fillRect(0, 0, 200, 200);

const actor = new ex.Actor({


A sprite is a view into a ImageSource and a projection into a final destination size.

const image = new ex.ImageSource('./img/myimage.png')
// keep in mind this wont work until the raw image is loaded
const sprite = new ex.Sprite({
  image: image,
  sourceView: {
    // Take a small slice of the source image starting at pixel (10, 10) with dimension 20 pixels x 20 pixels
    x: 10,
    y: 10,
    width: 20,
    height: 20,
  destSize: {
    // Optionally specify a different projected size, otherwise use the source
    width: 100,
    height: 100,

Many times a sprite is the exact same view and size as the source raw image so there is a quick static helper to do this

const image = new ex.ImageSource('./img/myimage.png')
// keep in mind this wont work until the image source is loaded
const sprite = image.toSprite()


Animations are a series of graphics that take a specific duration in milliseconds. Each of these units is called a "Frame". There are a few playing strategies as well to consider

export enum AnimationStrategy {
   * Animation ends without displaying anything
  End = 'end',
   * Animation loops to the first frame after the last frame
  Loop = 'loop',
   * Animation plays to the last frame, then backwards to the first frame, then repeats
  PingPong = 'pingpong',

   * Animation ends stopping on the last frame
  Freeze = 'freeze',

Animation frames can be created by hand in the following example

const animation = new ex.Animation({
  frames: [
      graphic: newSprite,
      duration: 500,
      graphic: circle,
      duration: 1000,
      graphic: rect,
      duration: 1500,
      graphic: triangle,
      duration: 2000,

Animations can be constructed quickly from ex.SpriteSheets

Character running sprite sheet

import runImageSrc from './player-run.png'
const game = new ex.Engine({
    width: 600,
    height: 400

const runImage = new ex.ImageSource(runImageSrc);

const runSheet = ex.SpriteSheet.fromImageSource({
    image: runImage,
    grid: {
        rows: 1,
        columns: 21,
        spriteWidth: 96,
        spriteHeight: 96

const runAnim = ex.Animation.fromSpriteSheet(runSheet, ex.Util.range(1, 10), 200);

const actor = new ex.Actor({
    pos: ex.vec(game.halfDrawWidth, game.halfDrawHeight)

Animations also emit events per frame, per loop, and per end (if it completes).

anim.on('loop', (a) => {
anim.on('frame', (f) => {
anim.on('ended', (a) => {


SpriteSheet is really an ordered collection of sprites from the same base image.

const spriteSheet = new ex.SpriteSheet({
  image: imageRun,
  sprites: [...]

If you spritesheet is a neat grid there is a static builder for you to slice up that image source. Most sprite sheets are tightly packed like so.

Some source spritesheets may have margin between sprites and an offset, like these playing cards from pixel art playing cards

const kennyCardsImage = new ex.ImageSource(kennyCardsImageSrc);

const spriteSheet = ex.SpriteSheet.fromImageSource({
    image: kennyCardsImage,
    grid: {
        rows: 4,
        columns: 14,
        spriteWidth: 42,
        spriteHeight: 60
    spacing: {
        // Optionally specify the offset from the top left of sheet to start parsing
        originOffset: { x: 11, y: 2 },
        // Optionally specify the margin between each sprite
        margin: { x: 23, y: 5}


A graphics group is an new graphic that draws a graphics in some relation to one another. This can be useful when you want to compose graphics together into a single graphic. Graphics groups do support all types of graphics including animations

const group = new ex.GraphicsGroup({
  members: [
      graphic: newSprite,
      pos: ex.vec(0, 0),
      graphic: newSprite,
      pos: ex.vec(50, 0),
      graphic: newSprite,
      pos: ex.vec(0, 50),
      graphic: text,
      pos: ex.vec(100, 20),
      graphic: circle,
      pos: ex.vec(50, 50),
      graphic: anim,
      pos: ex.vec(200, 200),
      graphic: triangle,
      pos: ex.vec(0, 200),


Rasters are a type of ex.Graphic built by constructing a bitmap (using CanvasRenderingContext2D) in memory which is then sent to the drawing context. This allows the use of features available to developers in the 2D canvas to produce Graphics for Excalibur. It's important to share or limit the number of Raster graphics due to their memory footprint in the browser.

Rasters are only rendered when they need to be, if no properties change on them, they are not recalculated. Rasters can be forced to be re-draw by calling .flagDirty().

See these useful raster implemenations

A custom raster can be built implementing the execute function, this is in fact how the Canvas is implemented.

export class MyRaster extends ex.Raster {
  constructor() {

  execute(ctx: CanvasRenderingContext2D): void {
    // my custom raster
    ctx.fillStyle = 'blue'
    ctx.fillRect(0, 0, 20, 20)

ex.Raster properties and methods

  • abstract execute(ctx: CanvasRenderingContext2D): void

    • This is the bitmap generation implementation, this will construct the desired bitmap on the CanvasRenderingContext2D.
  • rasterize(): void

    • This causes the underlying bitmap to be generated calling the execute() implementation in the process


Text is a raster graphic that can be used to draw text to the screen, all of the normal things that can be done with rasters and graphics can be done with text.

Text supports both normal OS/web fonts and Sprite fonts.


var text = new ex.Text({
  text: 'This is raster text ❤️',
  font: new ex.Font({ size: 30 }),


This specifies the font characteristics for a text raster.

Possible font options

export interface FontOptions {
  size?: number
  unit?: FontUnit
  family?: string
  style?: FontStyle
  bold?: boolean
  textAlign?: TextAlign
  baseAlign?: BaseAlign
  direction?: Direction
  shadow?: {
    blur?: number
    offset?: Vector
    color?: Color
var text = new ex.Text({
  text: 'This is raster text ❤️',
  font: new ex.Font({
    size: 30,
    unit: FontUnit.Px,
    family: 'sans-serif',
    style: FontStyle.Normal,
    bold: false,
    textAlign: TextAlign.Left,
    baseAlign: BaseAlign.Alphabetic,
    direction: Direction.LeftToRight,
    shadow: {
      blur: 2,
      offset: ex.Vec(2, 2),
      color: ex.Color.Black,


Sometimes you want to use text of your own creation from a spritesheet,

const spriteFontSheet = ex.SpriteSheet.fromImageSource({
  image: spriteFontImage,
  grid: {
    rows: 3,
    columns: 16,
    spriteWidth: 16,
    spriteHeight: 16

const spriteFont = new ex.SpriteFont({
    alphabet: '0123456789abcdefghijklmnopqrstuvwxyz,!\'&."?- ',
    caseInsensitive: true,
    spriteSheet: spriteFontSheet

const text = new ex.Text({
  text: 'This is sprite font text!!',
  font: spriteFont


Polygon creates a rastered polygon graphic given a set of points

const triangle = new ex.Polygon({
  points: [ex.vec(10 * 5, 0), ex.vec(0, 20 * 5), ex.vec(20 * 5, 20 * 5)],
  color: ex.Color.Yellow,


Circle creates a rastered circle graphic given a raidus

const circle = new ex.Circle({
  radius: 10,
  color: ex.Color.Red,


Rectangle creates a rastered rectange graphic given a width and height

const rect = new ex.Rectangle({
  width: 100,
  height: 100,
  color: ex.Color.Green,


The canvas is a special type of raster graphic that acts like a shim between direct CanvasRenderingContext2D drawing and the ExcaliburGraphicsContext

This type of raster is re-rendered every time the graphic is drawn, thus it should be used sparingly due to this inefficiency.

const canvas = new Canvas({
  draw: (ctx: CanvasRenderingContext2D) => {