Skip to content

Calibrate the Eye Tracker

In order for the eye tracker to provide accurate gaze point estimation, it needs to be calibrated. Eye tracker calibration is the process of

  1. Comparing the raw eye tracker signal (for example the point of intersection between the visual axis of both eyes)
  2. Updating a calibration model with the signals to minimize error
  3. Using the calibration model to calculate the gaze point.

There are several types and models of calibration. Obital makes sure that the best model is selected, and let's you pick between the following types:

  • Smooth Pursuit Calibration
  • Single Point Calibration (planned)
  • n-point Calibration (planned)

In this chapter, we're going to add a smooth pursuit calibration controller and calibrate the system.

Calibration Deterioration

Gaze tracking requires calibration, which means fitting a model to the user's eye. As the user moves around, environments change, and the device changes position, the calibration will deteriorate. This can negatively impact user experience. It can be avoided by informing the user about the problem, and asking them to re-calibrate if they feel that the accuracy is reduced. All custom View Controllers in Obital comes with a trackbox enabled by default. It makes sure that the user is always positioned in an approximately similar way.

Add a Calibration View Controller

Obital SDK comes with a series of example calibration view controllers. They make sure to display stimulus, and to create the calibration model in the back-end. We'll start by creating a Smooth Pursuits-based calibration procedure. It works by animating a moving circle, that covers the screen. When the user follows the circle with their gaze, the system is calibrated. Create a reference to the controller in you Main View Controller, and add it to the view:

// Class variable
var spCalibrationViewController = SPCalibrationViewController()

    override func viewDidLoad() {
        sessionManager = SessionManager()

    func createCalibrationController() {
        // Inject the session manager dependency in order for the view controller
        // to listen to gaze point updates.
        spCalibrationViewController.sessionManager = sessionManager
        // Set self to listen for changes in the calibration status.
        spCalibrationViewController.calibrationDelegate = self
        add(vc: spCalibrationViewController)

    /* --- Helper methods. You can add this to an extension instead. --- */
    // Adds the given view controller to the view hierachy and displays it.
    func add(vc: UIViewController) {
        vc.didMove(toParent: self)
    // Removes the view controller from the view hiearchy and deinitializes it.
    func remove(vc: UIViewController) {
        vc.willMove(toParent: nil)

Great! Now the calibration procedure is ready. We still need to know when the calibration is completed in order to react to it. Make an extension of your view controller conforming to SPCalibrationViewControllerDelegate:

extension MyViewController: SPCalibrationViewControllerDelegate {
    func didFinishCalibration(_ quality: NSMutableArray) {
        // Do whatever you want after calibration here. For example:
        // Set the star rating of a star rating view to show the calibration quality (1-5)
        if let quality = (quality.firstObject as? NSArray)?.firstObject as? Int32 {
            starRatingView.rating = Double(quality)
        // Remove the calibration view controller from the view:
        if let spVC = children.first(where: {$0 is SPCalibrationViewController}) {
            remove(vc: spVC)

Run the project. Start by following the moving target with your gaze. When the system registers your gaze, it will start calibrating the eye tracker by moving the target in a spiral pattern, covering the screen bounds. As the target reaches it end destination, didFinishCalibration() is called. The calibration view controller is gone, and the eye tracker is calibrated.

Visualize gaze tracking

It's time to see your gaze with your own eyes! We'll add a GazePointDotViewController to display a small red dot at the estimated gaze point. at the bottom of didFinishCalibration(), add:

func didFinishCalibration(_ quality: NSMutableArray) {
    // Previous code above.
    let gazePointViewController = GazePointDotViewController(sessionManager: sessionManager!)
    add(vc: gazePointViewController)

Run the project again. After calibration, you should now see the gaze point!