---
title: Mouse and Touch
slug: /pointers
section: Input
---

```twoslash include ex
/// <reference path="../src/engine/excalibur.d.ts" />
declare const engine: ex.Engine;
```

Excalibur handles mouse and touch input by abstracting event handling into a [[Engine.input|engine.input.pointers]] API that closely follows the [W3C Pointer Events](https://developer.mozilla.org/en-US/docs/Web/API/Pointer_events) spec. Excalibur normalizes both mouse and touch events to a single [[Input.PointerEvent|PointerEvent]]
that your game can subscribe to and handle.

## The Primary Pointer

There is always at least one [[PointerAbstraction|Pointer]] available ([[Input.PointerEventReceiver.primary|engine.input.pointers.primary]]) and
you can request multiple pointers to support multi-touch scenarios.

```ts twoslash
// @include: ex
// ---cut---
engine.input.pointers.primary;
```

Since a pointer normalizes both mouse and touch events, your game
automatically supports touch for the primary pointer by default. When
you handle the events, you can customize what your game does based on the type
of pointer, if applicable.

## Handling Pointer Events Globally

You can subscribe (or unsubscribe) to pointer events multiple ways using the [[Input.PointerEventReceiver.on|on]], [[Input.PointerEventReceiver.once|once]], or [[Input.PointerEventReceiver.off|off]] methods. Each method takes two arguments: the event name and the handler.

```ts twoslash
// @include: ex
// @noErrors
// ---cut---

engine.input.pointers.primary.o
//                             ^|


```

You can subscribe to the primary pointer, all pointers, or a specific pointer.

```ts twoslash
// @include: ex
// ---cut---

// Subscribe to the primary pointer
engine.input.pointers.primary.on;
//                            ^?

// Subscribe to a specific pointer (multi-touch)
engine.input.pointers.at(1).on;
//                          ^?

// Subscribe to all pointers (advanced)
engine.input.pointers.on;
//                    ^?
```

### Event Names

The event name is a string and can be one of the following:

- `down` - When a pointer is pressed down (any mouse button or finger press)
- `up` - When a pointer is lifted
- `move` - When a pointer moves (be wary of performance issues when subscribing to this)
- `cancel` - When a pointer event is canceled for some reason
- `wheel` - When a mousewheel is activated (trackpad scroll or mouse wheel)

### Event Handlers

A [[Input.PointerEvent|PointerEvent]] object (or in the case of `wheel`, a [[Input.WheelEvent|WheelEvent]]) is
passed to your handler which offers information about the pointer input being received.

```ts twoslash
// @include: ex
// ---cut---

// Subscribe to the primary pointer
engine.input.pointers.primary.on('down', function (evt) {});
//                                                 ^?
engine.input.pointers.primary.on('up', function (evt) {});
engine.input.pointers.primary.on('move', function (evt) {});
engine.input.pointers.primary.on('cancel', function (evt) {});
engine.input.pointers.primary.on('wheel', function (evt) {});
//                                                  ^?

// Subscribe to a specific pointer (multi-touch)
engine.input.pointers.at(1).on('down', function (evt) {});
engine.input.pointers.at(1).on('up', function (evt) {});
engine.input.pointers.at(1).on('move', function (evt) {});
engine.input.pointers.at(1).on('cancel', function (evt) {});
engine.input.pointers.at(1).on('wheel', function (evt) {});

// Subscribe to all pointers (advanced)
engine.input.pointers.on('down', function (evt) {});
engine.input.pointers.on('up', function (evt) {});
engine.input.pointers.on('move', function (evt) {});
engine.input.pointers.on('cancel', function (evt) {});
engine.input.pointers.on('wheel', function (evt) {});
```

### Querying the Last Position

If you don't wish to subscribe to events, you can also access the [[PointerAbstraction.lastPagePos]], [[PointerAbstraction.lastScreenPos]]
or [[PointerAbstraction.lastWorldPos]] coordinates ([[Vector]]) on the pointer you're targeting.

```ts twoslash
// @include: ex
// ---cut---
engine.input.pointers.primary.lastPagePos;
engine.input.pointers.primary.lastScreenPos;
engine.input.pointers.primary.lastWorldPos;
```

:::note
The value may be `null` if the Pointer was not active the last frame.
:::

### Scoping Events to Canvas vs. Document

You can customize how pointer events are handled by setting the [[EngineOptions.pointerScope]] option when creating a new [[Engine]] instance, like this:

```ts twoslash
// @noErrors
/// <reference path="../src/engine/excalibur.d.ts" />
// ---cut---
const game = new ex.Engine({
  pointerScope: ex.Input.PointerScope.C
//                                     ^|
});
```

The default scope is [[Input.PointerScope.Canvas|PointerScope.Canvas]]. For many games, this works really well. It scopes event handling to the game canvas.

This is useful if you don't care about events that occur outside the game.

You also have the option to handle _all_ pointer events in the browser window by using [[Input.PointerScope.Document|PointerScope.Document]], like this:

```ts twoslash
// @noErrors
/// <reference path="../src/engine/excalibur.d.ts" />
// ---cut---
const game = new ex.Engine({
  pointerScope: ex.Input.PointerScope.D
//                                     ^|
});
```

This is useful for handling complex input and having control over every interaction.

:::note When to Use Document Scope

One real-world example is dragging and gestures. Sometimes a player will drag their
finger outside your game and then into it, expecting it to work. If [[Input.PointerScope|PointerScope]]
is set to [[Input.PointerScope.Canvas|Canvas]] this will not work. If it is set to
[[Input.PointerScope.Document|Document]], it will.

:::

:::info What about HTML elements?
`PointerScope.Canvas` does not affect handling native HTML events like `click`, `mouseover`, `mouseout`, etc. on any HTML-based UI. It only applies to pointer events that are targeted to the HTML `<canvas>` element Excalibur uses to render your game.

However, `PointerScope.Document` will handle all pointer events in the browser window, including those targeted to HTML elements. If you overlay any HTML elements on top of your game, be aware that pointer events will also be handled by Excalibur.
:::

## Handling Pointer Events on Actors

[[Actor|Actors]] can handle pointer events targeted to them. By default, the collision geometry is used to test whether the pointer is
in or out of the actor. 

In the example below the actor has default box geometry of 100x100:

```ts twoslash
// @include: ex
// ---cut---
class MyActor extends ex.Actor {
  constructor() {
    super({
      pos: ex.vec(200, 200),
      width: 100,
      height: 100
    });

    this.on('pointerenter', () => {
      console.log('enter')
    });

    this.on('pointerleave', () => {
      console.log('leave')
    });
  }
}

```
:::warning
If your [[Actor]] has no geometry and only graphics you will need to enable graphics bounds testing.
:::

### Graphics Bounds Testing

Rather than using the collision geometry, you can use the graphics bounds to test whether the pointer is in or out of the actor. This is useful when you have an actor with no collision geometry and only graphics, or when the graphics have custom dimensions that don't match the collision geometry.

To enable graphics bounds testing for pointer events you can grab the [[Actor]]'s [[Input.PointerComponent|PointerComponent]] with `this.pointer` and set `useGraphicsBounds` to `true`.

```ts twoslash {4}
// @include: ex
// ---cut---
class MyGraphicsActor extends ex.Actor {
  constructor(image: ex.ImageSource) {
    super({pos: ex.vec(200, 200)});
    this.pointer.useGraphicsBounds = true;
    this.graphics.use(image.toSprite());

    this.on('pointerenter', () => {
      console.log('enter')
    });
    this.on('pointerleave', () => {
      console.log('leave')
    });
  }
}

```

### Actor Pointer Event List

Actors have the following **extra** events you can subscribe to:

- `pointerenter` - When a pointer enters the bounds of an actor
- `pointerleave` - When a pointer leaves the bounds of an actor
- `pointerdragstart` - When a pointer starts a drag on an actor
- `pointerdragmove` - When a pointer drags an actor
- `pointerdragend` - When a pointer ends a drag on an actor

## Checking the Pointer Type

The primary pointer can be a mouse, stylus, or single finger touch event. You
can inspect what type of pointer it is from the [[Input.PointerEvent|PointerEvent]] handled.

```ts twoslash
// @include: ex
// ---cut---
engine.input.pointers.primary.on('down', function (pe: ex.Input.PointerEvent) {
  if (pe.pointerType === ex.Input.PointerType.Mouse) {
    ex.Logger.getInstance().info('Mouse event:', pe);
  } else if (pe.pointerType === ex.Input.PointerType.Touch) {
    ex.Logger.getInstance().info('Touch event:', pe);
  }
});
```

## Multiple Pointers (Multi-Touch)

When there is more than one pointer detected on the screen,
this is considered multi-touch. For example, pressing one finger,
then another, will create two pointers. If you lift a finger,
the first one remains and the second one disappears.

You can handle multi-touch by subscribing to however many pointers
you would like to support. If a pointer doesn't yet exist, it will
be created. You do not need to check if a pointer exists. If it does
exist, it will propagate events, otherwise it will remain idle.

Excalibur does not impose a limit to the amount of pointers you can
subscribe to, so by all means, support all 10 fingers.

:::note
There is no way to identify touches after they happen; you can only
know that there are _n_ touches on the screen at once.
:::

```ts twoslash
// @include: ex
// ---cut---
function paint(color: ex.Color) {
  // create a handler for the event
  return function (pe: ex.Input.PointerEvent) {
    if (pe.pointerType === ex.Input.PointerType.Touch) {
      engine.graphicsContext.drawRectangle(pe.worldPos, 5, 5, color);
    }
  };
}
engine.input.pointers.at(0).on('move', paint(ex.Color.Blue)); // 1st finger
engine.input.pointers.at(1).on('move', paint(ex.Color.Red)); // 2nd finger
engine.input.pointers.at(2).on('move', paint(ex.Color.Green)); // 3rd finger
```
