Controller API Basics

This page goes into a little more in-depth description of the API for using the Daydream Controller.

You must have Daydream hardware to use the Controller API.

The GvrController class

To use the Controller API, you must have a game object in your scene with the GvrController behavior. To do that, simply add the GvrControllerMain prefab to your scene, which is in the Assets/Cardboard/Prefabs/Controller folder.

The main entry point to the Controller API is the GvrController class.

The Controller API is a polling-style API which gives you access to the controller's state and events. You can easily access it from the Update() method in any of your game objects. For example:

void Update() {

  // Example: get controller's current orientation:
  Quaternion ori = GvrController.Orientation;

  // If you want a vector that points in the direction of the controller
  // you can just multiply this quat by Vector3.forward:
  Vector3 vector = ori * Vector3.forward;

  // ...or you can just change the rotation of some entity on your scene
  // (e.g. the player's arm) to match the controller's orientation
  playerArmObject.transform.localRotation = ori;

  // Example: check if touchpad was just touched
  if (GvrController.TouchDown) {
    // Do something.
    // TouchDown is true for 1 frame after touchpad is touched.

  // Example: check if app button was just released.
  if (GvrController.AppButtonUp) {
    // Do something.
    // AppButtonUp is true for 1 frame after touchpad is touched.

  // Example: check the position of the user's finger on the touchpad
  if (GvrController.IsTouching) {
    Vector2 touchPos = GvrController.TouchPos;
    // Do something.


The touchpad state and buttons are reported through two kinds of properties: the state properties (which report whether or not the touchpad or the button is currently being pressed) and the transient properties (which report whether the touchpad or button was just pressed or just released), much like the distinction between Input.KeyDown and Input.Key.

So, for example, when the user presses the App button for a few seconds and then releases, AppButtonDown will be true for one frame when the user starts pressing the button, AppButton will be true for every frame while the user is holding down the App button, and AppButtonUp will be true for one frame immediately after the user releases the button.


The GvrController class exposes the following values:

  • IsTouching: true if user is currently touching the touchpad

  • TouchDown: true for 1 frame after user starts touching the touchpad

  • TouchUp: true for 1 frame after user stops touching the touchpad

  • TouchPos: position of the current touch, if touching. If not touching, then this is the position of the last touch (when the finger left the touchpad).

The touch position is given as a Vector2 where X and Y range from 0 to 1. (0, 0) is the top left of the touchpad and (1, 1) is the bottom right of the touchpad.


The orientation is given by the GvrController.Orientation property. It is a quaternion in the standard Unity world space. This means that you can easily obtain, for example, a vector pointing in the same direction as the controller by multiplying it by Vector3.forward:

// v is a vector that points in the same direction as the controller is pointing.
Vector3 v = GvrController.Orientation * Vector3.forward;

You can also directly set the rotation of an object in your scene to match the controller's pose:

// Sets the pose of myObject to match the controller's pose.
// The object must be initially in the "forward" pose.
myObject.transform.localRotation = GvrController.Orientation;

For this to work correctly, however, make sure the object is initially in the "forward" pose, as that's the controller's neutral pose.


The controller reports gyroscope readings through the Gyro property. It is a Vector3 that gives the current angular speed of the controller about each of its three local axes.

  • The controller's local coordinate system moves with the controller, it is not fixed to the scene.

  • The controller's local X axis points to the right of the controller.

  • The controller's local Y axis points perpendicularly up from the controller's top surface.

  • The controller's local Z axis points along the controller toward the back.

  • The gyroscope readings are given in radians per second.

  • The sign of the angular speed is given by the left-hand rule.

As an example, if the controller is being rotated clockwise on a tabletop at a rate of a half revolution per second, the gyroscope reading will be approximately (0.00, 3.14, 0.00), as that is a rotation of PI radians per second about the Y axis and the sign is positive by the left-hand rule.

It's important to notice that the gyroscope reports speed, not position. So if the controller is sitting perfectly still, the gyroscope reading will always be close to (0.00, 0.00, 0.00) regardless of which direction it is pointing.


The controller reports accelerometer readings through the Accel property. It is a Vector3 that gives the acceleration (with gravity) felt by the controller along each of its three local axes. The reading is reported in meters per second squared.

If the controller is lying flat on a tabletop on Earth, the only acceleration felt by the controller is that of Earth's gravity, which is along the controller's Y axis. So the accelerometer reading will be approximately (0.00, 9.80, 0.00). If the controller is in free fall (or in outer space), it will read (0.00, 0.00, 0.00).

It's useful to think about the equivalency principle of gravity and acceleration: a controller at rest on a tabletop on Earth will feel the same acceleration that it would feel on the floor of a spaceship in zero gravity that was accelerating at 9.8m/s^2.


The controller reports the following button properties:

  • ClickButton: True if the Click button (a touchpad click) is currently being pressed.

  • ClickButtonDown: True for 1 frame after user starts pressing the Click button.

  • ClickButtonUp: True for 1 frame after user stops pressing the Click button.

  • AppButton: True if the App button is currently being pressed.

  • AppButtonDown: True for 1 frame after user starts pressing the App button.

  • AppButtonUp: True for 1 frame after user stops pressing the App button.


By default, recentering is automatically handled by the SDK. To recenter:

  1. Press and hold the Home button on the controller.

  2. Hold the controller in front of you and level with the horizon, for at least one second.

  3. Release the Home button.

If you want to react to recentering, you can read the GvrController.Recentered property:

  • Recentered: True for 1 frame after a recenter happens.

When a recentering happens, both the headset and the controller will immediately start reporting poses in the new (recentered) coordinate system.

For apps that don't have a natural forward facing direction (360˚ experiences) and for which automatic camera recentering doesn't make sense or would lead to a confusing user experience, it's possible to prevent headset recentering by adding the GvrRecenterOnlyController script to any game object in your scene.

Checking the controller status

You can also query for controller status. The status is given by the GvrController.State property. Here is an example of how to process that value in order to display a message to the user and pause the game when the controller disconnects:

GameObject statusCanvas;  // reference to a canvas in your scene
Text statusText;  // reference to the text field in that canvas

switch (GvrController.State) {
  case GvrConnectionState.Connected:
    Time.timeScale = 1;
  case GvrConnectionState.Disconnected:
    statusText.text = "Waiting for controller...";
    Time.timeScale = 0;
  case GvrConnectionState.Scanning:
    statusText.text = "Scanning for controller...";
    Time.timeScale = 0;
  case GvrConnectionState.Connecting:
    statusText.text = "Connecting to controller...";
    Time.timeScale = 0;
  case GvrConnectionState.Error:
    statusText.text = "ERROR: " + GvrController.ErrorDescription;
    Time.timeScale = 0;
    statusText.text = "Unknown status.";
    Time.timeScale = 0;