Skip to content

A step-by-step introduction to making games and interactive media with the Pixi.js rendering engine.

Notifications You must be signed in to change notification settings

kittykatattack/learningPixi

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 

Repository files navigation

Learning Pixi

A step-by-step introduction to making games and interactive media with the Pixi rendering engine. Updated for Pixi v5.3.10. Chinese version here: Pixi官方教程中文版. If you like this tutorial, you'll love the book, which contains 80% more content!.

Table of contents

  1. Introduction
  2. Setting up
    1. Installing Pixi
  3. Creating the stage and renderer
  4. Pixi sprites
  5. Loading images into the texture cache
  6. Displaying sprites
    1. Using Aliases
    2. A little more about loading things
      1. Make a sprite from an ordinary JavaScript Image object or Canvas
      2. Assigning a name to a loaded file
      3. Monitoring load progress
      4. More about Pixi's loader
  7. Positioning sprites
  8. Size and scale
  9. Rotation
  10. Make a sprite from a tileset sub-image
  11. Using a texture atlas
  12. Loading the texture atlas
  13. Creating sprites from a loaded texture atlas
  14. Moving Sprites
  15. Using velocity properties
  16. Game states
  17. Keyboard Movement
  18. Grouping Sprites
    1. Local and global positions
    2. Using a ParticleContainer to group sprites
  19. Pixi's Graphic Primitives
    1. Rectangle
    2. Circles
    3. Ellipses
    4. Rounded rectangles
    5. Lines
    6. Polygons
  20. Displaying text
  21. Collision detection
    1. The hitTestRectangle function
  22. Case study: Treasure Hunter
    1. Initialize the game in the setup function
      1. Creating the game scenes
      2. Making the dungeon, door, explorer and treasure
      3. Making the blob monsters
      4. Making health bar
      5. Making message text
    2. Playing the game
    3. Moving the explorer
      1. Containing movement
    4. Moving the monsters
    5. Checking for collisions
    6. Reaching the exit door and ending game
  23. More about sprites
  24. Taking it further
    1.Hexi
    2.BabylonJS
  25. Supporting this project

Introduction

Pixi is an extremely fast 2D sprite rendering engine. What does that mean? It means that it helps you to display, animate and manage interactive graphics so that it's easy for you to make games and applications using JavaScript and other HTML5 technologies. It has a sensible, uncluttered API and includes many useful features, like supporting texture atlases and providing a streamlined system for animating sprites (interactive images). It also gives you a complete scene graph so that you can create hierarchies of nested sprites (sprites inside sprites), as well as letting you attach mouse and touch events directly to sprites. And, most importantly, Pixi gets out of your way so that you can use as much or as little of it as you want to, adapt it to your personal coding style, and integrate it seamlessly with other useful frameworks.

Pixi’s API is actually a refinement of a well-worn and battle-tested API pioneered by Macromedia/Adobe Flash. Old-skool Flash developers will feel right at home. Other current sprite rendering frameworks use a similar API: CreateJS, Starling, Sparrow and Apple’s SpriteKit. The strength of Pixi’s API is that it’s general-purpose: it’s not a game engine. That’s good because it gives you total expressive freedom to make anything you like, and wrap your own custom game engine around it.

In this tutorial you’re going to find out how to combine Pixi’s powerful image rendering features and scene graph to start making games. But Pixi isn't just for games - you can use these same techniques to create any interactive media applications. That means apps for phones!

What do you need to know before you get started with this tutorial?

You should have a reasonable understanding of HTML and JavaScript. You don't have to be an expert, just an ambitious beginner with an eagerness to learn. If you don't know HTML and JavaScript, the best place to start learning it is this book:

Foundation Game Design with HTML5 and JavaScript

I know for a fact that it's the best book, because I wrote it!

There are also some good internet resources to help get you started:

Khan Academy: Computer Programming

Code Academy: JavaScript

Choose whichever best suits your learning style.

Ok, got it? Do you know what JavaScript variables, functions, arrays and objects are and how to use them? Do you know what JSON data files are? Have you used the Canvas Drawing API?

To use Pixi, you'll also need to run a webserver in your root project directory. Do you know what a webserver is and how to launch one in your project folder? The best way is to use node.js and then to install the extremely easy to use http-server. However, you need to be comfortable working with the Unix command line if you want to do that. You can learn how to use Unix in this video and, when you're finished, follow it with this video. You should learn how to use Unix - it only takes a couple of hours to learn and is a really fun and easy way to interact with your computer.

But if you don't want to mess around with the command line just yet, try the Mongoose webserver:

Or, just write all your code using the excellent Brackets text editor. Brackets automatically launches a webserver and browser for you when you click the lightning bolt button in its main workspace.

Now if you think you're ready, read on!

(Request to readers: this is a living document. If you have any questions about specific details or need any of the content clarified, please create an issue in this GitHub repository and I'll update the text with more information.)

Setting up

Before you start writing any code, create a folder for your project, and launch a webserver in the project's root directory. If you aren't running a webserver, Pixi won't work.

Next, you need to install Pixi.

Installing Pixi

The version used for this introduction is v5.3.10 and you can find the pixi.min.js file either in this repository's pixi folder or on Pixi's release page for v5.3.10. Or, you can get the latest version from Pixi's main release page.

This one file is all you need to use Pixi. You can ignore all the other files in the repository: you don't need them.

Next, create a basic HTML page, and use a <script> tag to link the pixi.min.js file that you've just downloaded. The <script> tag's src should be relative to your root directory where your webserver is running. Your <script> tag might look something like this:

<script src="pixi.min.js"></script>

Here's a basic HTML page that you could use to link Pixi and test that it's working. (This assumes that the pixi.min.js is in a subfolder called pixi):

<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <title>Hello World</title>
</head>
<body>
  <script src="pixi/pixi.min.js"></script>
  <script type="text/javascript">
    let type = "WebGL";
    if (!PIXI.utils.isWebGLSupported()) {
      type = "canvas";
    }

    PIXI.utils.sayHello(type);
  </script>
</body>
</html>

If Pixi is linking correctly, something like this will be displayed in your web browser's JavaScript console by default:

      PixiJS 5.3.10 - * WebGL * http://www.pixijs.com/  ♥♥♥ 

Creating the Pixi Application and stage

Now you can start using Pixi!

But how?

The first step is to create a rectangular display area that you can start displaying images on. Pixi has an Application object that creates this for you. It automatically generates an HTML <canvas> element and figures out how to display your images on the canvas. You then need to create a special Pixi Container object called the stage. As you'll see ahead, this stage object is going to be used as the root container that holds all the things you want Pixi to display.

Here’s the code you need to write to create an app Pixi Application and stage. Add this code to your HTML document between the <script> tags:

//Create a Pixi Application
const app = new PIXI.Application({width: 256, height: 256});

//Add the canvas that Pixi automatically created for you to the HTML document
document.body.appendChild(app.view);

This is the most basic code you need write to get started using Pixi. It produces a black 256 pixel by 256 pixel canvas element and adds it to your HTML document. Here's what this looks like in a browser when you run this code.

Basic display

Yay, a black square!

PIXI.Application's only argument is a single object called the options object. In this example its width and height properties are set to determine the width and height of the canvas, in pixels. You can set many more optional properties inside this options object; here's how you could use it to set anti-aliasing, transparency and resolution:

const app = new PIXI.Application({ 
    width: 256,         // default: 800
    height: 256,        // default: 600
    antialias: true,    // default: false
    transparent: false, // default: false
    resolution: 1       // default: 1
  }
);

If you're happy with Pixi's default settings, you don't need to set any of these options. But, if you need to, see Pixi's documentation on PIXI.Application.

What do those options do? antialias smoothes the edges of fonts and graphic primitives. (WebGL anti-aliasing isn’t available on all platforms, so you’ll need to test this on your game’s target platform.) transparent makes the canvas background transparent. resolution makes it easier to work with displays of varying resolutions and pixel densities. Setting the resolutions is a little outside the scope of this tutorial, but check out Mat Grove's explanation about how to use resolution for all the details. But usually, just keep resolution at 1 for most projects and you'll be fine.

Pixi's renderer object will default to WebGL, which is good, because WebGL is incredibly fast, and lets you use some spectacular visual effects that you’ll learn all about ahead. The Canvas renderer was removed in version 5 and above, but Pixi provides a separate version named pixi.js-legacy which re-adds support for the canvas renderer should you need it.

If you need to change the background color of the canvas after you’ve created it, set the app.renderer object’s backgroundColor property to any hexadecimal color value:

app.renderer.backgroundColor = 0x061639;

If you want to find the width or the height of the renderer, use app.renderer.view.width and app.renderer.view.height.

To change the size of the canvas, use the renderer’s resize method, and supply any new width and height values. But, to make sure the canvas is resized to match the resolution, set autoDensity to true.

app.renderer.autoDensity = true;
app.renderer.resize(512, 512);

If you want to make the canvas fill the entire window and adjust automatically if it is resized, you can use some CSS styling along with the resizeTo property, providing it an element to scale to, such as window as the value. resizeTo can also be passed with the rest of your options when creating your Pixi application.

app.renderer.view.style.position = "absolute";
app.renderer.view.style.display = "block";
app.renderer.autoDensity = true;
app.resizeTo = window;

But, if you do that, make sure you also set the default padding and margins to 0 on all your HTML elements with this bit of CSS code:

<style>* {padding: 0; margin: 0}</style>

(The asterisk, *, in the code above, is the CSS "universal selector", which just means "all the tags in the HTML document".)

If you want the canvas to scale proportionally to any browser window size, you can use this custom scaleToWindow function.

Pixi sprites

Now that you have a renderer, you can start adding images to it. Anything you want to be made visible in the renderer has to be added to a special Pixi object called the stage. You can access this special stage object like this:

app.stage

The stage is a Pixi Container object. You can think of a container as a kind of empty box that will group together and store whatever you put inside it. The stage object is the root container for all the visible things in your scene. Whatever you put inside the stage will be rendered on the canvas. Right now the stage is empty, but soon we're going to start putting things inside it. (You can read more about Pixi's Container objects here).

(Important: because the stage is a Pixi Container it has the same properties and methods as any other Container object. But, although the stage has width and height properties, they don't refer to the size of the rendering window. The stage's width and height properties just tell you the area occupied by the things you put inside it - more on that ahead!)

So what do you put on the stage? Special image objects called sprites. Sprites are basically just images that you can control with code. You can control their position, size, and a host of other properties that are useful for making interactive and animated graphics. Learning to make and control sprites is really the most important thing about learning to use Pixi. If you know how to make sprites and add them to the stage, you're just a small step away from starting to make games.

Pixi has a Sprite class that is a versatile way to make game sprites. There are three main ways to create them:

  • From a single image file.
  • From a sub-image on a tileset. A tileset is a single, big image that includes all the images you'll need in your game.
  • From a texture atlas (A JSON file that defines the size and position of an image on a tileset.)

You’re going to learn all three ways, but, before you do, let’s find out what you need to know about images before you can display them with Pixi.

Loading images into the texture cache

Because Pixi renders the image on the GPU with WebGL, the image needs to be in a format that the GPU can process. A WebGL-ready image is called a texture. Before you can make a sprite display an image, you need to convert an ordinary image file into a WebGL texture. To keep everything working fast and efficiently under the hood, Pixi uses a texture cache to store and reference all the textures your sprites will need. The names of the textures are strings that match the file locations of the images they refer to. That means if you have a texture that was loaded from "images/cat.png", you could find it in the texture cache like this:

PIXI.utils.TextureCache["images/cat.png"];

The textures are stored in a WebGL compatible format that’s efficient for Pixi’s renderer to work with. You can then use Pixi’s Sprite class to make a new sprite using the texture.

const texture = PIXI.utils.TextureCache["images/anySpriteImage.png"];
const sprite = new PIXI.Sprite(texture);

But how do you load the image file and convert it into a texture? Use Pixi’s built-in Loader object.

Pixi's powerful Loader object is all you need to load any kind of image. Here’s how to use it to load an image and call a function called setup when the image has finished loading:

PIXI.Loader.shared
  .add("images/anyImage.png")
  .load(setup);

function setup() {
  //This code will run when the loader has finished loading the image
}

Pixi’s development team recommends that if you use the loader, you should create the sprite by referencing the texture in the Loader’s resources object, like this:

const sprite = new PIXI.Sprite(
  PIXI.Loader.shared.resources["images/anyImage.png"].texture
);

Here’s an example of some complete code you could write to load an image, call the setup function, and create a sprite from the loaded image:

PIXI.Loader.shared
  .add("images/anyImage.png")
  .load(setup);

function setup() {
  const sprite = new PIXI.Sprite(
    PIXI.Loader.shared.resources["images/anyImage.png"].texture
  );
}

This is the general format we’ll be using to load images and create sprites in this tutorial.

You can load multiple images at the same time by listing them with chainable add methods, like this:

PIXI.Loader.shared
  .add("images/imageOne.png")
  .add("images/imageTwo.png")
  .add("images/imageThree.png")
  .load(setup);

Better yet, just list all the files you want to load in an array inside a single add method, like this:

PIXI.Loader.shared
  .add([
    "images/imageOne.png",
    "images/imageTwo.png",
    "images/imageThree.png"
  ])
  .load(setup);

The Loader also lets you load JSON files, which you'll learn about ahead.

Displaying sprites

After you've loaded an image, and used it to make a sprite, you need to add the sprite to Pixi's stage with the stage.addChild method, like this:

app.stage.addChild(cat);

Remember that the stage is the main container that holds all of your sprites.

Important: you won't be able to see any of your sprites unless you add them to the stage!

Before we continue, let's look at a practical example of how to use what you've just learnt to display a single image. In the examples/images folder you'll find a 64 by 64 pixel PNG image of a cat.

Basic display

Here's all the JavaScript code you need to load the image, create a sprite, and display it on Pixi's stage:

//Create a Pixi Application
const app = new PIXI.Application({ 
    width: 256, 
    height: 256,                       
    antialias: true, 
    transparent: false, 
    resolution: 1
  }
);

//Add the canvas that Pixi automatically created for you to the HTML document
document.body.appendChild(app.view);

//load an image and run the `setup` function when it's done
PIXI.Loader.shared
  .add("images/cat.png")
  .load(setup);

//This `setup` function will run when the image has loaded
function setup() {

  //Create the cat sprite
  const cat = new PIXI.Sprite(PIXI.Loader.shared.resources["images/cat.png"].texture);
  
  //Add the cat to the stage
  app.stage.addChild(cat);
}

When this code runs, here's what you'll see:

Cat on the stage

Now we're getting somewhere!

If you ever need to remove a sprite from the stage, use the removeChild method:

app.stage.removeChild(anySprite);

But usually setting a sprite’s visible property to false will be a simpler and more efficient way of making sprites disappear.

anySprite.visible = false;

Using aliases

You can save yourself a little typing and make your code more readable by creating short-form aliases for the Pixi objects and methods that you use frequently. For example, is prefixing PIXI to all of Pixi's objects starting to bog you down? If you think so, create a shorter alias that points to it. For example, here's how you can create an alias to the TextureCache object:

const TextureCache = PIXI.utils.TextureCache;

Then, use that alias in place of the original, like this:

const texture = TextureCache["images/cat.png"];

In addition to letting you write slightly more succinct code, using aliases has an extra benefit: it helps to buffer you from Pixi's frequently changing API. If Pixi's API changes in future versions - which it will! - you just need to update these aliases to Pixi objects and methods in one place, at the beginning of your program, instead of every instance where they're used throughout your code. So when Pixi's development team decides they want to rearrange the furniture a bit, you'll be one step ahead of them!

To see how to do this, let's re-write the code we wrote to load an image and display it, using aliases for all the Pixi objects and methods.

//Aliases
const Application = PIXI.Application,
    loader = PIXI.Loader.shared,
    resources = PIXI.Loader.shared.resources,
    Sprite = PIXI.Sprite;

//Create a Pixi Application
const app = new Application({ 
    width: 256, 
    height: 256,                       
    antialias: true, 
    transparent: false, 
    resolution: 1
  }
);

//Add the canvas that Pixi automatically created for you to the HTML document
document.body.appendChild(app.view);

//load an image and run the `setup` function when it's done
loader
  .add("images/cat.png")
  .load(setup);

//This `setup` function will run when the image has loaded
function setup() {

  //Create the cat sprite
  const cat = new Sprite(resources["images/cat.png"].texture);
  
  //Add the cat to the stage
  app.stage.addChild(cat);
}

Most of the examples in this tutorial will use aliases for Pixi objects that follow this same model. Unless otherwise stated, you can assume that all the code examples that follow use aliases like these.

This is all you need to know to start loading images and creating sprites.

A little more about loading things

The format I've shown you above is what I suggest you use as your standard template for loading images and displaying sprites. So, you can safely ignore the next few paragraphs and jump straight to the next section, "Positioning sprites." But Pixi's Loader object is quite sophisticated and includes a few features that you should be aware of, even if you don't use them on a regular basis. Let's look at some of the most useful.

Make a sprite from an ordinary JavaScript Image object or Canvas

For optimization and efficiency it’s always best to make a sprite from a texture that’s been pre-loaded into Pixi’s texture cache. But if for some reason you need to make a texture from a regular JavaScript Image object, you can do that using Pixi’s BaseTexture and Texture classes:

const base = new PIXI.BaseTexture(anyImageObject),
    texture = new PIXI.Texture(base),
    sprite = new PIXI.Sprite(texture);

You can use BaseTexture.from if you want to make a texture from any existing canvas element:

const base = new PIXI.BaseTexture.from(anyCanvasElement);

If you want to change the texture the sprite is displaying, use the texture property. Set it to any Texture object, like this:

anySprite.texture = PIXI.utils.TextureCache["anyTexture.png"];

You can use this technique to interactively change the sprite’s appearance if something significant happens to it in the game.

Assigning a name to a loading file

It's possible to assign a unique name to each resource you want to load. Just supply the name (a string) as the first argument in the add method. For example, here's how to name an image of a cat as catImage.

PIXI.Loader.shared
  .add("catImage", "images/cat.png")
  .load(setup);

This creates an object called catImage in Loader.shared.resources. That means you can create a sprite by referencing the catImage object, like this:

const cat = new PIXI.Sprite(PIXI.Loader.shared.resources.catImage.texture);

However, I recommend you don't use this feature! That's because you'll have to remember all names you gave each loaded files, as well as make sure you don't accidentally use the same name more than once. Using the file path name, as we've done in previous examples is simpler and less error prone.

Monitoring load progress

Pixi's Loader has a special progress event that will call a customizable function that will run each time a file loads. progress events are called by the Loader's onProgress method, like this:

PIXI.Loader.shared.onProgress.add(loadProgressHandler);

Here's how to include the onProgress method in the loading chain, and call a user-definable function called loadProgressHandler each time a file loads.

PIXI.Loader.shared.onProgress.add(loadProgressHandler)
PIXI.Loader.shared
  .add([
    "images/one.png",
    "images/two.png",
    "images/three.png"
  ])
  .load(setup);

function loadProgressHandler() {
  console.log("loading"); 
}

function setup() {
  console.log("setup");
}

Each time one of the files loads, the progress event calls loadProgressHandler to display "loading" in the console. When all three files have loaded, the setup function will run. Here's the output of the above code in the console:

loading
loading
loading
setup

That's neat, but it gets better. You can also find out exactly which file has loaded and what percentage of overall files are have currently loaded. You can do this by adding optional loader and resource parameters to the loadProgressHandler, like this:

function loadProgressHandler(loader, resource) { /*...*/ }

You can then use resource.url to find the file that's currently loaded. (Use resource.name if you want to find the optional name that you might have assigned to the file, as the first argument in the add method.) And you can use loader.progress to find what percentage of total resources have currently loaded. Here's some code that does just that.

PIXI.Loader.shared.onProgress.add(loadProgressHandler);
PIXI.Loader.shared
  .add([
    "images/one.png",
    "images/two.png",
    "images/three.png"
  ])
  .load(setup);

function loadProgressHandler(loader, resource) {

  //Display the file `url` currently being loaded
  console.log("loading: " + resource.url); 

  //Display the percentage of files currently loaded
  console.log("progress: " + loader.progress + "%"); 

  //If you gave your files names as the first argument 
  //of the `add` method, you can access them like this
  //console.log("loading: " + resource.name);
}

function setup() {
  console.log("All files loaded");
}

Here's what this code will display in the console when it runs:

loading: images/one.png
progress: 33.333333333333336%
loading: images/two.png
progress: 66.66666666666667%
loading: images/three.png
progress: 100%
All files loaded

That's really cool, because you could use this as the basis for creating a loading progress bar.

(Note: There are additional properties you can access on the resource object. resource.error will tell you of any possible error that happened while trying to load a file. resource.data lets you access the file's raw binary data.)

More about Pixi's Loader

Pixi's Loader is ridiculously feature-rich and configurable. Let's take a quick bird's-eye view of its usage to get you started.

The loader's chainable add method takes 4 basic arguments:

add(name, url, optionObject, callbackFunction);

Here's what the loader's source code documentation has to say about these parameters:

name (string): The name of the resource to load. If it's not passed, the url is used.
url (string): The url for this resource, relative to the baseUrl of the loader.
options (object literal): The options for the load.
options.crossOrigin (Boolean): Is the request cross-origin? The default is to determine automatically.
options.loadType: How should the resource be loaded? The default value is Resource.LOAD_TYPE.XHR.
options.xhrType: How should the data being loaded be interpreted when using XHR? The default value is Resource.XHR_RESPONSE_TYPE.DEFAULT
callbackFunction: The function to call when this specific resource completes loading.

The only one of these arguments that's required is the url (the file that you want to load.)

Here are some examples of some ways you could use the add method to load files. These first ones are what the docs call the loader's "normal syntax":

.add('key', 'http://...', function () {})
.add('http://...', function () {})
.add('http://...')

And these are examples of the loader's "object syntax":

.add({
  name: 'key2',
  url: 'http://...'
}, function () {})

.add({
  url: 'http://...'
}, function () {})

.add({
  name: 'key3',
  url: 'http://...',
  onComplete: function () {}
})

.add({
  url: 'https://...',
  onComplete: function () {},
  crossOrigin: true
})

You can also pass the add method an array of objects, or urls, or both:

.add([
  {
    name: 'key4',
    url: 'http://...',
    onComplete: function () {}
  },
  {
    url: 'http://...',
    onComplete: function () {}
  },
  'http://...'
]);

(Note: If you ever need to reset the loader to load a new batch of files, call the loader's reset method: PIXI.Loader.shared.reset();)

Pixi's Loader has many more advanced features, including options to let you load and parse binary files of all types. This is not something you'll need to do on a day-to-day basis, and way outside the scope of this tutorial, so make sure to check out the loader's GitHub repository for more information.

Positioning sprites

Now that you know how to create and display sprites, let's find out how to position and resize them.

In the earlier example the cat sprite was added to the stage at the top left corner. The cat has an x position of 0 and a y position of 0. You can change the position of the cat by changing the values of its x and y properties. Here's how you can center the cat in the stage by setting its x and y property values to 96.

cat.x = 96;
cat.y = 96;

Add these two lines of code anywhere inside the setup function, after you've created the sprite.

function setup() {

  //Create the `cat` sprite
  const cat = new Sprite(resources["images/cat.png"].texture);

  //Change the sprite's position
  cat.x = 96;
  cat.y = 96;

  //Add the cat to the stage so you can see it
  app.stage.addChild(cat);
}

(Note: In this example, Sprite is an alias for PIXI.Sprite, TextureCache is an alias for PIXI.utils.TextureCache, and resources is an alias for PIXI.Loader.shared.resources as described earlier. I'll be using aliases that follow this same format for all Pixi objects and methods in the example code from now on.)

These two new lines of code will move the cat 96 pixels to the right, and 96 pixels down. Here's the result:

Cat centered on the stage

The cat's top left corner (its left ear) represents its x and y anchor point. To make the cat move to the right, increase the value of its x property. To make the cat move down, increase the value of its y property. If the cat has an x value of 0, it will be at the very left side of the stage. If it has a y value of 0, it will be at the very top of the stage.

Cat centered on the stage - diagram

Instead of setting the sprite's x and y properties independently, you can set them together in a single line of code, like this:

sprite.position.set(x, y);

Size and scale

You can change a sprite's size by setting its width and height properties. Here's how to give the cat a width of 80 pixels and a height of 120 pixels.

cat.width = 80;
cat.height = 120;

Add those two lines of code to the setup function, like this:

function setup() {

  //Create the `cat` sprite
  const cat = new Sprite(resources["images/cat.png"].texture);

  //Change the sprite's position
  cat.x = 96;
  cat.y = 96;

  //Change the sprite's size
  cat.width = 80;
  cat.height = 120;

  //Add the cat to the stage so you can see it
  app.stage.addChild(cat);
}

Here's the result:

Cat's height and width changed

You can see that the cat's position (its top left corner) didn't change, only its width and height.

Cat's height and width changed - diagram

Sprites also have scale.x and scale.y properties that change the sprite's width and height proportionately. Here's how to set the cat's scale to half size:

cat.scale.x = 0.5;
cat.scale.y = 0.5;

Scale values are numbers between 0 and 1 that represent a percentage of the sprite's size. 1 means 100% (full size), while 0.5 means 50% (half size). You can double the sprite's size by setting its scale values to 2, like this:

cat.scale.x = 2;
cat.scale.y = 2;

Pixi has an alternative, concise way for you set sprite's scale in one line of code using the scale.set method.

cat.scale.set(0.5, 0.5);

If that appeals to you, use it!

Rotation

You can make a sprite rotate by setting its rotation property to a value in radians.

cat.rotation = 0.5;

But around which point does that rotation happen?

You've seen that a sprite's top left corner represents its x and y position. That point is called the anchor point. If you set the sprite’s rotation property to something like 0.5, the rotation will happen around the sprite’s anchor point. This diagram shows what effect this will have on our cat sprite.

Rotation around anchor point - diagram

You can see that the anchor point, the cat’s left ear, is the center of the imaginary circle around which the cat is rotating. What if you want the sprite to rotate around its center? Change the sprite’s anchor point so that it’s centered inside the sprite, like this:

cat.anchor.x = 0.5;
cat.anchor.y = 0.5;

The anchor.x and anchor.y values represent a percentage of the texture’s dimensions, from 0 to 1 (0% to 100%). Setting it to 0.5 centers the texture over the point. The location of the point itself won’t change, just the way the texture is positioned over it.

This next diagram shows what happens to the rotated sprite if you center its anchor point.

Rotation around centered anchor point - diagram

You can see that the sprite’s texture shifts up and to the left. This is an important side-effect to remember!

Just like with position and scale, you can set the anchor’s x and y values with one line of code like this:

cat.anchor.set(x, y);

Sprites also have a pivot property which works in a similar way to anchor. pivot sets the position of the sprite's x/y origin point. If you change the pivot point and then rotate the sprite, it will rotate around that origin point. For example, the following code will set the sprite's pivot.x point to 32, and its pivot.y point to 32

cat.pivot.set(32, 32);

Assuming that the sprite is 64x64 pixels, the sprite will now rotate around its center point. But remember: if you change a sprite's pivot point, you've also changed its x/y origin point.

So, what's the difference between anchor and pivot? They're really similar! anchor shifts the origin point of the sprite's image texture, using a 0 to 1 normalized value. pivot shifts the origin of the sprite's x and y point, using pixel values. Which should you use? It's up to you. Just play around with both of them and see which you prefer.

Make a sprite from a tileset sub-image

You now know how to make a sprite from a single image file. But, as a game designer, you’ll usually be making your sprites using tilesets (also known as spritesheets.) Pixi has some convenient built-in ways to help you do this. A tileset is a single image file that contains sub-images. The sub-images represent all the graphics you want to use in your game. Here's an example of a tileset image that contains game characters and game objects as sub-images.

An example tileset

The entire tileset is 192 by 192 pixels. Each image is in its own 32 by 32 pixel grid cell. Storing and accessing all your game graphics on a tileset is a very processor and memory efficient way to work with graphics, and Pixi is optimized for this.

You can capture a sub-image from a tileset by defining a rectangular area that's the same size and position as the sub-image you want to extract. Here's an example of the rocket sub-image that’s been extracted from the tileset.

Rocket extracted from tileset

Let's look at the code that does this. First, load the tileset.png image with Pixi’s Loader, just as you've done in earlier examples.

loader
  .add("images/tileset.png")
  .load(setup);

Next, when the image has loaded, use a rectangular sub-section of the tileset to create the sprite’s image. Here's the code that extracts the sub image, creates the rocket sprite, and positions and displays it on the canvas.

function setup() {

  //Create the `tileset` sprite from the texture
  const texture = TextureCache["images/tileset.png"];

  //Create a rectangle object that defines the position and
  //size of the sub-image you want to extract from the texture
  //(`Rectangle` is an alias for `PIXI.Rectangle`)
  const rectangle = new Rectangle(192, 128, 64, 64);

  //Tell the texture to use that rectangular section
  texture.frame = rectangle;

  //Create the sprite from the texture
  const rocket = new Sprite(texture);

  //Position the rocket sprite on the canvas
  rocket.x = 32;
  rocket.y = 32;

  //Add the rocket to the stage
  app.stage.addChild(rocket);
  
  //Render the stage   
  app.renderer.render(app.stage);
}

How does this work?

Pixi has a built-in Rectangle object (PIXI.Rectangle) that is a general-purpose object for defining rectangular shapes. It takes four arguments. The first two arguments define the rectangle's x and y position. The last two define its width and height. Here's the format for defining a new Rectangle object.

const rectangle = new Rectangle(x, y, width, height);

The rectangle object is just a data object; it's up to you to decide how you want to use it. In our example we're using it to define the position and area of the sub-image on the tileset that we want to extract. Pixi textures have a useful property called frame that can be set to any Rectangle objects. The frame crops the texture to the dimensions of the Rectangle. Here's how to use frame to crop the texture to the size and position of the rocket.

const rectangle = new Rectangle(192, 128, 64, 64);
texture.frame = rectangle;

You can then use that cropped texture to create the sprite:

const rocket = new Sprite(texture);

And that's how it works!

Because making sprite textures from a tileset is something you’ll do with great frequency, Pixi has a more convenient way to help you do this - let's find out what that is next.

Using a texture atlas

If you’re working on a big, complex game, you’ll want a fast and efficient way to create sprites from tilesets. This is where a texture atlas becomes really useful. A texture atlas is a JSON data file that contains the positions and sizes of sub-images on a matching tileset PNG image. If you use a texture atlas, all you need to know about the sub-image you want to display is its name. You can arrange your tileset images in any order and the JSON file will keep track of their sizes and positions for you. This is really convenient because it means the sizes and positions of tileset images aren’t hard-coded into your game program. If you make changes to the tileset, like adding images, resizing them, or removing them, just re-publish the JSON file and your game will use that data to display the correct images. You won’t have to make any changes to your game code.

Pixi is compatible with a standard JSON texture atlas format that is output by a popular software tool called Texture Packer. Texture Packer’s “Essential” license is free. Let’s find out how to use it to make a texture atlas, and load the atlas into Pixi. (You don’t have to use Texture Packer. Similar tools, like Shoebox or spritesheet.js, output PNG and JSON files in a standard format that is compatible with Pixi.)

First, start with a collection of individual image files that you'd like to use in your game.

Image files

(All the images in this section were created by Lanea Zimmerman. You can find more of her artwork here. Thanks, Lanea!)

Next, open Texture Packer and choose JSON Hash as the framework type. Drag your images into Texture Packer's workspace. (You can also point Texture Packer to any folder that contains your images.) It will automatically arrange the images on a single tileset image, and give them names that match their original image names.

Image files

(If you're using the free version of Texture Packer, set Algorithm to Basic, set Trim mode to None, set Extrude to 0, set Size constraints to Any size and slide the PNG Opt Level all the way to the left to 0. These are the basic settings that will allow the free version of Texture Packer to create your files without any warnings or errors.)

When you’re done, click the Publish button. Choose the file name and location, and save the published files. You’ll end up with 2 files: a PNG file and a JSON file. In this example my file names are treasureHunter.json and treasureHunter.png. To make your life easier, just keep both files in your project’s images folder. (You can think of the JSON file as extra metadata for the image file, so it makes sense to keep both files in the same folder.) The JSON file describes the name, size and position of each of the sub-images in the tileset. Here’s an excerpt that describes the blob monster sub-image.

"blob.png":
{
	"frame": {"x":55,"y":2,"w":32,"h":24},
	"rotated": false,
	"trimmed": false,
	"spriteSourceSize": {"x":0,"y":0,"w":32,"h":24},
	"sourceSize": {"w":32,"h":24},
	"pivot": {"x":0.5,"y":0.5}
},

The treasureHunter.json file also contains “dungeon.png”, “door.png”, "exit.png", and "explorer.png" properties each with similar data. Each of these sub-images are called frames. Having this data is really helpful because now you don’t need to know the size and position of each sub-image in the tileset. All you need to know is the sprite’s frame id. The frame id is just the name of the original image file, like "blob.png" or "explorer.png".

Among the many advantages to using a texture atlas is that you can easily add 2 pixels of padding around each image (Texture Packer does this by default.) This is important to prevent the possibility of texture bleed. Texture bleed is an effect that happens when the edge of an adjacent image on the tileset appears next to a sprite. This happens because of the way your computer's GPU (Graphics Processing Unit) decides how to round fractional pixels values. Should it round them up or down? This will be different for each GPU. Leaving 1 or 2 pixels spacing around images on a tileset makes all images display consistently.

(Note: If you have two pixels of padding around a graphic, and you still notice a strange "off by one pixel" glitch in the way Pixi is displaying it, try changing the texture's scale mode algorithm. Here's how: texture.baseTexture.scaleMode = PIXI.SCALE_MODES.NEAREST;. These glitches can sometimes happen because of GPU floating point rounding errors.)

Now that you know how to create a texture atlas, let's find out how to load it into your game code.

Loading the texture atlas

To get the texture atlas into Pixi, load it using Pixi’s Loader. If the JSON file was made with Texture Packer, the Loader will interpret the data and create a texture from each frame on the tileset automatically. Here’s how to use the Loader to load the treasureHunter.json file. When it has loaded, the setup function will run.

loader
  .add("images/treasureHunter.json")
  .load(setup);

Each image on the tileset is now an individual texture in Pixi’s cache. You can access each texture in the cache with the same name it had in Texture Packer (“blob.png”, “dungeon.png”, “explorer.png”, etc.).

Creating sprites from a loaded texture atlas

Pixi gives you three general ways to create a sprite from a texture atlas:

  1. Using TextureCache:
const texture = TextureCache["frameId.png"],
    sprite = new Sprite(texture);
  1. If you’ve used Pixi’s Loader to load the texture atlas, use the loader’s resources:
const sprite = new Sprite(
  resources["images/treasureHunter.json"].textures["frameId.png"]
);
  1. That’s way too much typing to do just to create a sprite! So I suggest you create an alias called id that points to texture’s altas’s textures object, like this:
const id = resources["images/treasureHunter.json"].textures; 

Then you can just create each new sprite like this:

const sprite = new Sprite(id["frameId.png"]);

Much better!

Here's how you could use these three different sprite creation techniques in the setup function to create and display the dungeon, explorer, and treasure sprites.

//Define variables that might be used in more 
//than one function
let dungeon, explorer, treasure, id;

function setup() {

  //There are 3 ways to make sprites from textures atlas frames

  //1. Access the `TextureCache` directly
  const dungeonTexture = TextureCache["dungeon.png"];
  dungeon = new Sprite(dungeonTexture);
  app.stage.addChild(dungeon);

  //2. Access the texture using through the loader's `resources`:
  explorer = new Sprite(
    resources["images/treasureHunter.json"].textures["explorer.png"]
  );
  explorer.x = 68;

  //Center the explorer vertically
  explorer.y = app.stage.height / 2 - explorer.height / 2;
  app.stage.addChild(explorer);

  //3. Create an optional alias called `id` for all the texture atlas 
  //frame id textures.
  id = resources["images/treasureHunter.json"].textures; 
  
  //Make the treasure box using the alias
  treasure = new Sprite(id["treasure.png"]);
  app.stage.addChild(treasure);

  //Position the treasure next to the right edge of the canvas
  treasure.x = app.stage.width - treasure.width - 48;
  treasure.y = app.stage.height / 2 - treasure.height / 2;
  app.stage.addChild(treasure);
}

Here's what this code displays:

Explorer, dungeon and treasure

The stage dimensions are 512 by 512 pixels, and you can see in the code above that the app.stage.height and app.stage.width properties are used to align the sprites. Here's how the explorer's y position is vertically centered:

explorer.y = app.stage.height / 2 - explorer.height / 2;

Learning to create and display sprites using a texture atlas is an important benchmark. So before we continue, let's take a look at the code you could write to add the remaining sprites: the blobs and exit door, so that you can produce a scene that looks like this:

All the texture atlas sprites

Here's the entire code that does all this. I've also included the HTML code so you can see everything in its proper context. (You'll find this working code in the examples/spriteFromTextureAtlas.html file in this repository.) Notice that the blob sprites are created and added to the stage in a loop, and assigned random positions.

<!doctype html>
  <html>
  <head>
  <meta charset="utf-8">
  <title>Make a sprite from a texture atlas</title>
  </head>
  <body>
    <script src="../pixi/pixi.min.js"></script>
    <script>

      //Aliases
      const Application = PIXI.Application,
          Container = PIXI.Container,
          loader = PIXI.Loader.shared,
          resources = PIXI.Loader.shared.resources,
          TextureCache = PIXI.utils.TextureCache,
          Sprite = PIXI.Sprite,
          Rectangle = PIXI.Rectangle;

      //Create a Pixi Application
      const app = new Application({ 
          width: 512, 
          height: 512,                       
          antialias: true, 
          transparent: false, 
          resolution: 1
        }
      );

      //Add the canvas that Pixi automatically created for you to the HTML document
      document.body.appendChild(app.view);

      //load a JSON file and run the `setup` function when it's done
      loader
        .add("images/treasureHunter.json")
        .load(setup);

      //Define variables that might be used in more 
      //than one function
      let dungeon, explorer, treasure, door, id;

      function setup() {

        //There are 3 ways to make sprites from textures atlas frames

        //1. Access the `TextureCache` directly
        const dungeonTexture = TextureCache["dungeon.png"];
        dungeon = new Sprite(dungeonTexture);
        app.stage.addChild(dungeon);

        //2. Access the texture using throuhg the loader's `resources`:
        explorer = new Sprite(
          resources["images/treasureHunter.json"].textures["explorer.png"]
        );
        explorer.x = 68;

        //Center the explorer vertically
        explorer.y = app.stage.height / 2 - explorer.height / 2;
        app.stage.addChild(explorer);

        //3. Create an optional alias called `id` for all the texture atlas 
        //frame id textures.
        id = resources["images/treasureHunter.json"].textures; 
        
        //Make the treasure box using the alias
        treasure = new Sprite(id["treasure.png"]);
        app.stage.addChild(treasure);

        //Position the treasure next to the right edge of the canvas
        treasure.x = app.stage.width - treasure.width - 48;
        treasure.y = app.stage.height / 2 - treasure.height / 2;
        app.stage.addChild(treasure);

        //Make the exit door
        door = new Sprite(id["door.png"]); 
        door.position.set(32, 0);
        app.stage.addChild(door);

        //Make the blobs
        const numberOfBlobs = 6,
            spacing = 48,
            xOffset = 150;

        //Make as many blobs as there are `numberOfBlobs`
        for (let i = 0; i < numberOfBlobs; i++) {

          //Make a blob
          const blob = new Sprite(id["blob.png"]);

          //Space each blob horizontally according to the `spacing` value.
          //`xOffset` determines the point from the left of the screen
          //at which the first blob should be added.
          const x = spacing * i + xOffset;

          //Give the blob a random y position
          //(`randomInt` is a custom function - see below)
          const y = randomInt(0, app.stage.height - blob.height);

          //Set the blob's position
          blob.x = x;
          blob.y = y;

          //Add the blob sprite to the stage
          app.stage.addChild(blob);
        }
      }

      //The `randomInt` helper function
      function randomInt(min, max) {
        return Math.floor(Math.random() * (max - min + 1)) + min;
      }

    </script>
  </body>
  </html>

You can see in the code above that all the blobs are created using a for loop. Each blob is spaced evenly along the x axis like this:

const x = spacing * i + xOffset;
blob.x = x;

spacing has a value 48, and xOffset has a value of 150. What this means is the first blob will have an x position of 150. This offsets it from the left side of the stage by 150 pixels. Each subsequent blob will have an x value that's 48 pixels greater than the blob created in the previous iteration of the loop. This creates an evenly spaced line of blob monsters, from left to right, along the dungeon floor.

Each blob is also given a random y position. Here's the code that does this:

const y = randomInt(0, stage.height - blob.height);
blob.y = y;

The blob's y position could be assigned any random number between 0 and 512, which is the value of stage.height. This works with the help of a custom function called randomInt. randomInt returns a random number that's within a range between any two numbers you supply.

randomInt(lowestNumber, highestNumber);

That means if you want a random number between 1 and 10, you can get one like this:

const randomNumber = randomInt(1, 10);

Here's the randomInt function definition that does all this work:

function randomInt(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min;
}

randomInt is a great little function to keep in your back pocket for making games - I use it all the time.

Moving Sprites

You now know how to display sprites, but how do you make them move? That's easy: create a looping function using Pixi's ticker This is called a game loop. Any code you put inside the game loop will update 60 times per second. Here's some code you could write to make the cat sprite move to the right at a rate of 1 pixel per frame.

function setup() {

  //Start the game loop by adding the `gameLoop` function to
  //Pixi's `ticker` and providing it with a `delta` argument.
  app.ticker.add((delta) => gameLoop(delta));
}

function gameLoop(delta) {

  //Move the cat 1 pixel 
  cat.x += 1;
}

If you run this bit of code, you'll see the sprite gradually move to the right side of the stage.

Moving sprites

That's because each time the gameLoop runs, it adds 1 to the cat's x position.

cat.x += 1;

Any function you add to Pixi's ticker will be called 60 times per second. You can see that the function is provided a delta argument - what's that?

The delta value represents the amount of fractional lag between frames. You can optionally add it to the cat's position, to make the cat's animation independent of the frame rate. Here's how:

cat.x += 1 + delta;

Whether or not you choose to add this delta value is largely an aestheic choice. And the effect will only really be noticeable if your animation is struggling to keep up with a consistent 60 frames per second display rate (which might happen, for example, if it's running on a slow device). The rest of the examples in this tutorial won't use this delta value, but feel free to use it in your own work if you wish.

You don't have to use Pixi's ticker to create a game loop. If you prefer, just use requestAnimationFrame, like this:

function gameLoop() {

  //Call this `gameLoop` function on the next screen refresh
  //(which happens 60 times per second)
  requestAnimationFrame(gameLoop);

  //Move the cat
  cat.x += 1;
}

//Start the loop
gameLoop();

It entirely up to you which style you prefer.

That's really all there is to it! Just change any sprite property by small increments inside the loop, and they'll animate over time. If you want the sprite to animate in the opposite direction (to the left), just give it a negative value, like -1.

You'll find this code in the movingSprites.html file - here's the complete code:

//Aliases
const Application = PIXI.Application,
    Container = PIXI.Container,
    loader = PIXI.Loader.shared,
    resources = PIXI.Loader.shared.resources,
    TextureCache = PIXI.utils.TextureCache,
    Sprite = PIXI.Sprite,
    Rectangle = PIXI.Rectangle;

//Create a Pixi Application
const app = new Application({ 
    width: 256, 
    height: 256,                       
    antialias: true, 
    transparent: false, 
    resolution: 1
  }
);

//Add the canvas that Pixi automatically created for you to the HTML document
document.body.appendChild(app.view);

loader
  .add("images/cat.png")
  .load(setup);

//Define any variables that are used in more than one function
let cat;

function setup() {

  //Create the `cat` sprite 
  cat = new Sprite(resources["images/cat.png"].texture);
  cat.y = 96; 
  app.stage.addChild(cat);
 
  //Start the game loop 
  app.ticker.add((delta) => gameLoop(delta));
}

function gameLoop(delta) {

  //Move the cat 1 pixel 
  cat.x += 1;
  
  //Optionally use the `delta` value
  //cat.x += 1 + delta;
}

(Notice that the cat variable needs to be defined outside the setup and gameLoop functions so that you can access it inside both of them.)

You can animate a sprite's scale, rotation, or size - whatever! You'll see many more examples of how to animate sprites ahead.

Using velocity properties

To give you more flexibility, it's a good idea to control a sprite's movement speed using two velocity properties: vx and vy. vx is used to set the sprite's speed and direction on the x axis (horizontally). vy is used to set the sprite's speed and direction on the y axis (vertically). Instead of changing a sprite's x and y values directly, first update the velocity variables, and then assign those velocity values to the sprite. This is an extra bit of modularity that you'll need for interactive game animation.

The first step is to create vx and vy properties on your sprite, and give them an initial value.

cat.vx = 0;
cat.vy = 0;

Setting vx and vy to 0 means that the sprite isn't moving.

Next, in the game loop, update vx and vy with the velocity at which you want the sprite to move. Then assign those values to the sprite's x and y properties. Here's how you could use this technique to make the cat sprite move down and to right at one pixel each frame:

function setup() {

  //Create the `cat` sprite 
  cat = new Sprite(resources["images/cat.png"].texture);
  cat.y = 96; 
  cat.vx = 0;
  cat.vy = 0;
  app.stage.addChild(cat);
 
  //Start the game loop
  app.ticker.add((delta) => gameLoop(delta));
}

function gameLoop(delta) {

  //Update the cat's velocity
  cat.vx = 1;
  cat.vy = 1;

  //Apply the velocity values to the cat's 
  //position to make it move
  cat.x += cat.vx;
  cat.y += cat.vy;
}

When you run this code, the cat will move down and to the right at one pixel per frame:

Moving sprites

What if you want to make the cat move in a different direction? To make the cat move to the left, give it a vx value of -1. To make it move up, give the cat a vy value of -1. To make the cat move faster, give it larger vx and vy values, like 3, 5, -2, or -4.

You'll see ahead how modularizing a sprite's velocity with vx and vy velocity properties helps with keyboard and mouse pointer control systems for games, as well as making it easier to implement physics.

Game states

As a matter of style, and to help modularize your code, I recommend structuring your game loop like this:

//Set the game state
state = play;
 
//Start the game loop 
app.ticker.add((delta) => gameLoop(delta));

function gameLoop(delta) {

  //Update the current game state:
  state(delta);
}

function play(delta) {

  //Move the cat 1 pixel to the right each frame
  cat.vx = 1;
  cat.x += cat.vx;
}

You can see that the gameLoop is calling a function called state 60 times per second. What is the state function? It's been assigned to play. That means all the code in the play function will also run at 60 times per second.

Here's how the code from the previous example can be re-factored to this new model:

//Define any variables that are used in more than one function
let cat, state;

function setup() {

  //Create the `cat` sprite 
  cat = new Sprite(resources["images/cat.png"].texture);
  cat.y = 96; 
  cat.vx = 0;
  cat.vy = 0;
  app.stage.addChild(cat);

  //Set the game state
  state = play;
 
  //Start the game loop 
  app.ticker.add((delta) => gameLoop(delta));
}

function gameLoop(delta) {

  //Update the current game state:
  state(delta);
}

function play(delta) {

  //Move the cat 1 pixel to the right each frame
  cat.vx = 1;
  cat.x += cat.vx;
}

Yes, I know, this is a bit of head-swirler! But, don't let it scare you and spend a minute or two walking through in your mind how those functions are connected. As you'll see ahead, structuring your game loop like this will make it much, much easier to do things like switching game scenes and levels.

Keyboard Movement

With just a little more work you can build a simple system to control a sprite using the keyboard. To simplify your code, I suggest you use this custom function called keyboard that listens for and captures keyboard events.

function keyboard(value) {
  const key = {};
  key.value = value;
  key.isDown = false;
  key.isUp = true;
  key.press = undefined;
  key.release = undefined;
  //The `downHandler`
  key.downHandler = (event) => {
    if (event.key === key.value) {
      if (key.isUp && key.press) {
        key.press();
      }
      key.isDown = true;
      key.isUp = false;
      event.preventDefault();
    }
  };

  //The `upHandler`
  key.upHandler = (event) => {
    if (event.key === key.value) {
      if (key.isDown && key.release) {
        key.release();
      }
      key.isDown = false;
      key.isUp = true;
      event.preventDefault();
    }
  };

  //Attach event listeners
  const downListener = key.downHandler.bind(key);
  const upListener = key.upHandler.bind(key);
  
  window.addEventListener("keydown", downListener, false);
  window.addEventListener("keyup", upListener, false);
  
  // Detach event listeners
  key.unsubscribe = () => {
    window.removeEventListener("keydown", downListener);
    window.removeEventListener("keyup", upListener);
  };
  
  return key;
}

The keyboard function is easy to use. Create a new keyboard object like this:

const keyObject = keyboard(keyValue);

Its one argument is the key value that you want to listen for. Here's a list of keys.

Then assign press and release methods to the keyboard object like this:

keyObject.press = () => {
  //key object pressed
};
keyObject.release = () => {
  //key object released
};

Keyboard objects also have isDown and isUp Boolean properties that you can use to check the state of each key.

Don't forget to remove event listeners by using the unsubscribe method:

keyObject.unsubscribe();

Take a look at the keyboardMovement.html file in the examples folder to see how you can use this keyboard function to control a sprite using your keyboard's arrow keys. Run it and use the left, up, down, and right arrow keys to move the cat around the stage.

Keyboard movement

Here's the code that does all this:

//Define any variables that are used in more than one function
let cat, state;

function setup() {

  //Create the `cat` sprite 
  cat = new Sprite(resources["images/cat.png"].texture);
  cat.y = 96; 
  cat.vx = 0;
  cat.vy = 0;
  app.stage.addChild(cat);

  //Capture the keyboard arrow keys
  const left = keyboard("ArrowLeft"),
      up = keyboard("ArrowUp"),
      right