General API concepts¶
The API singleton¶
Regardless of the language you are using, the Beam Eye Tracker SDK provides a way to
create an API instance, which is the main entry point for getting data and communicating with
the Beam Eye Tracker application. This API instance is only a wrapper to an underlying singleton
allocated for your entire application. Logically, it communicates
with the only instance of the Beam Eye Tracker application running in the user’s PC.
The API singleton is further designed to be:
Light-weight on client code. Every function call should return quickly to the thread of execution, unless explicitly stated otherwise (see Synchronous data access).
Thread-safe: You can call it from multiple threads without having to worry about race conditions.
RAII: Allocating the
APIinitializes communications with the Beam Eye Tracker and destroying all instances should release the singleton resources.
The friendly name, specified to the API instantiation, is used to inform the user which application
is connected to the Beam Eye Tracker.
The second term is the ViewportGeometry, which is explained at the Viewport section.
#include "eyeware/beam_eye_tracker_c.h"
EW_BET_API_HANDLE api = EW_BET_NULL_HANDLE;
EW_BET_API_CreateResult result =
EW_BET_API_Create("MyImmersiveGame", {1.0f, 1.0f, 1.0f, 1.0f}, &api);
// if API creation was successful
if (result == EW_BET_API_CREATE_SUCCESS) {
// ...do something with the API...
// .. release the API
EW_BET_API_Destroy(api);
}
#include "eyeware/beam_eye_tracker.h"
eyeware::beam_eye_tracker::API api("MyImmersiveGame", {1.0f, 1.0f, 1.0f, 1.0f});
viewport_geometry = beam_eye_tracker.ViewportGeometry()
viewport_geometry.point_00 = beam_eye_tracker.Point(0, 0)
viewport_geometry.point_11 = beam_eye_tracker.Point(0, 0)
bet_api = beam_eye_tracker.API("MyImmersiveGame", viewport_geometry)
import { createSession } from "@eyeware/beam-eye-tracker";
const api = await createSession("MyImmersiveGame", {
point00: { x: 0, y: 0 },
point11: { x: 1920, y: 1080 },
});
// ...do something with the API...
api.destroy();
API creation failures¶
The native SDK client is a loader. Creating an API instance also requires a
compatible Beam-provided runtime library from the local Beam Eye Tracker
installation. If Beam is not installed, is too old, or the runtime candidate
cannot be verified or loaded, API creation fails before a usable handle or object
is returned.
In the C API, EW_BET_API_Create returns one of the named
EW_BET_API_CreateResult values:
EW_BET_API_CREATE_SUCCESS: creation succeeded.EW_BET_API_CREATE_INVALID_ARGUMENT: the friendly name or output handle argument is invalid.EW_BET_API_CREATE_RUNTIME_UNAVAILABLE: no supported, trusted Beam-provided runtime could be loaded.EW_BET_API_CREATE_INITIALIZATION_FAILED: the runtime was reached, but API implementation initialization failed.
On failure, do not use the output EW_BET_API_HANDLE.
For C++, Python, and C#, construction reports the same situations as exceptions: invalid friendly names are reported as argument errors, while unavailable Beam runtimes or lower-level initialization failures are reported as runtime or operation failures.
The TrackingStateSet object¶
One of the main goals of the API object is to provide a stream of TrackingStateSet objects. The
TrackingStateSet is a container for the sub-structures that provide either the raw tracking user state,
or preprocessed data to easily implement interactive features in games.
In particular, these sub-structures are:
UserState, which holds the raw tracking data for the user, including the user’s gaze on screen and head pose. See Real-time tracking.GameImmersiveHUDState, which holds the parameters to implement the immersive HUD. See Dynamic HUDs.SimGameCameraState, which holds the parameters to implement the immersive camera controls at the webcam framerate. However, we recommend to use theAPIto sample a smoother output at the rendering rate for optimal frame pacing. See In-game camera control.FoveatedRenderingState, which holds the parameters to implement foveated rendering. See Foveated rendering.
Timestamps¶
Each TrackingStateSet is associated to a Timestamp value. As expected, it indicates the time
in seconds for when the TrackingStateSet object was created or updated, whether
all fields are updated or not. However, it’s important to note that if the timestamp contains the NULL_DATA_TIMESTAMP
value, it means that the data held by the TrackingStateSet object is invalid for whatever reason, whether
the tracking is not activated, the user is not being tracked, etc, and thus should be discarded.
The above applies to the full TrackingStateSet object you will further notice that each
sub-structure held by it (e.g. UserState, SimGameCameraState, GameImmersiveHUDState)
contains its own Timestamp field. Although in most cases, each sub-structure will have the same timestamp
value as the one associated to the TrackingStateSet object, or the other sub-structures, you should not make
that assumption, as a future-proof integration should assume that each-substructure could have its own frequency of updates.
Moreover, each individual sub-structure could also hold a NULL_DATA_TIMESTAMP value, indicating that the given
data is invalid for whatever reason, for example, the specific feature is disabled.
The TrackingDataReceptionStatus object¶
Through the API instance, you can query the current TrackingDataReceptionStatus to check if frame-by-frame data from the Beam Eye
Tracker is being received given that bi-directional communication is established.
It is important to note that this status is independent of whether the user is being successfully tracked or not - it only indicates if the connection to the Beam Eye Tracker is active and data is flowing. It also indicate if the Beam Eye Tracker is being automatically started/activated (see Launching the Beam Eye Tracker from your game or application).
For more details on the different values of the TrackingDataReceptionStatus object, see EW_BET_TrackingDataReceptionStatus.
Data Access Methods¶
The SDK supports three methods for accessing the stream of TrackingStateSet objects: synchronous or wait-style, polling and asynchronous.
Synchronous data access¶
Synchronous data access is convenient in case you want to get access to the latest TrackingStateSet at low
latency, and it is fine to block the current thread while waiting for data updates. A typical scenario is when your application (or at least the current thread) uses the tracking data updates
as the key event that triggers further processing/recording/interactions, etc.
The following example demonstrates how to use the synchronous or wait-style data access method, with the key functionality
being the use of the wait_for_new_tracking_state_set method, which uses a Timestamp object
as a synchronization token. JavaScript uses the same timeout concept, but returns a Promise
(await waitForNewTrackingStateSet(timeoutMs)) so that the browser main thread remains responsive.
1/**
2 * Copyright (c) 2025, Eyeware Tech SA.
3 *
4 * All rights reserved.
5 *
6 * @brief This sample demonstrates how to use the synchronous data access method.
7 *
8 * Note on the in-game camera signal: if your end goal is to feed an in-game camera from the
9 * smoothed sim camera output, prefer @ref API::sample_smooth_sim_game_camera_state called from
10 * your render loop over reading TrackingStateSet::sim_game_camera_state(). The former is
11 * smoothed at the rate the consumer queries; the latter is a snapshot at the tracking rate.
12 * The TrackingStateSet path remains valid for other data (user state, HUD signals, foveated
13 * rendering) and is illustrated here for completeness. See the game_engine_integration sample
14 * for the recommended pattern.
15 */
16#include "../bet_sample_utils.h"
17#include "eyeware/beam_eye_tracker.h"
18#include <memory>
19
20int main() {
21 eyeware::beam_eye_tracker::API bet_api =
22 create_bet_api_or_exit("Sync Sample", eyeware::beam_eye_tracker::ViewportGeometry());
23
24 // We hold a timestamp instance useful to sync the data reception.
25 eyeware::beam_eye_tracker::Timestamp last_update_timestamp_sec{
26 eyeware::beam_eye_tracker::NULL_DATA_TIMESTAMP};
27
28 std::unique_ptr<eyeware::beam_eye_tracker::TrackingStateSet> last_received_tracking_state_set;
29
30 eyeware::beam_eye_tracker::TrackingDataReceptionStatus previous_status =
31 bet_api.get_tracking_data_reception_status();
32
33 print_tracking_data_reception_status(previous_status);
34
35 int count = 0;
36 // Access 1 minute of data (assuming a 30fps webcam)
37 while (count < 30 * 60) {
38 const unsigned int wait_timeout_ms = 1000;
39
40 // Follow up with the status of the tracking data reception.
41 eyeware::beam_eye_tracker::TrackingDataReceptionStatus status =
42 bet_api.get_tracking_data_reception_status();
43 print_tracking_data_reception_status_if_changed(previous_status, status);
44 previous_status = status;
45
46 // Wait for a new frame. If this function returns true, then there is a new frame and
47 // last_update_timestamp_sec is updated.
48 if (bet_api.wait_for_new_tracking_state_set(last_update_timestamp_sec, wait_timeout_ms)) {
49 // This is how we access the latest TrackingStateSet.
50 // It is only movable, thus this is a way to retain it.
51 last_received_tracking_state_set =
52 std::make_unique<eyeware::beam_eye_tracker::TrackingStateSet>(
53 std::move(bet_api.get_latest_tracking_state_set()));
54
55 print_latest_tracking_state_set(*last_received_tracking_state_set,
56 last_update_timestamp_sec);
57
58 // The SimGameCameraState held by the TrackingStateSet is a snapshot at the tracking
59 // rate. For an in-game camera consumer it is preferable to query
60 // bet_api.sample_smooth_sim_game_camera_state() from the render loop instead. See
61 // the game_engine_integration sample for the recommended pattern.
62 print_sim_game_camera_state(bet_api,
63 last_received_tracking_state_set->sim_game_camera_state());
64
65 count++;
66 }
67 }
68
69 return 0;
70}
1# Copyright (c) 2025, Eyeware Tech SA.
2#
3# All rights reserved.
4#
5# This sample demonstrates how to use the synchronous data access method.
6#
7# Synchronous data access is convenient in case you want to get a TrackingStateSet at low
8# latency, and it is fine to block the thread while waiting for data updates.
9#
10# Note on the in-game camera signal: if your end goal is to feed an in-game camera from the
11# smoothed sim camera output, prefer API.sample_smooth_sim_game_camera_state called from your
12# render loop over reading TrackingStateSet.sim_game_camera_state(). The former is smoothed at
13# the rate the consumer queries; the latter is a snapshot at the tracking rate. The
14# TrackingStateSet path remains valid for other data (user state, HUD signals, foveated
15# rendering) and is illustrated here for completeness.
16import sys
17import os
18
19sys.path.append(os.path.join(os.path.dirname(__file__), ".."))
20
21import bet_add_to_python_path
22
23from eyeware import beam_eye_tracker
24from bet_sample_utils import (
25 create_bet_api_or_exit,
26 print_tracking_data_reception_status,
27 print_latest_tracking_state_set,
28 print_sim_game_camera_state,
29 print_tracking_data_reception_status_if_changed,
30)
31
32
33def main():
34 # Initialize API with empty viewport geometry
35 viewport_geometry = beam_eye_tracker.ViewportGeometry()
36 viewport_geometry.point_00 = beam_eye_tracker.Point(0, 0)
37 viewport_geometry.point_11 = beam_eye_tracker.Point(0, 0)
38
39 bet_api = create_bet_api_or_exit("Python Sync Sample", viewport_geometry)
40
41 # We hold a timestamp instance useful to sync the data reception
42 last_update_timestamp_sec = beam_eye_tracker.NULL_DATA_TIMESTAMP()
43
44 last_received_tracking_state_set = None
45 previous_status = bet_api.get_tracking_data_reception_status()
46 print_tracking_data_reception_status(previous_status)
47
48 count = 0
49 # Access 1 minute of data (assuming a 30fps webcam)
50 while count < 30 * 60:
51 wait_timeout_ms = 1000
52
53 # Follow up with the status of the tracking data reception
54 status = bet_api.get_tracking_data_reception_status()
55 print_tracking_data_reception_status_if_changed(previous_status, status)
56 previous_status = status
57
58 # Wait for a new frame. If this function returns true, then there is a new frame and
59 # last_update_timestamp_sec is updated
60 if bet_api.wait_for_new_tracking_state_set(last_update_timestamp_sec, wait_timeout_ms):
61 # This is how we access the latest TrackingStateSet
62 last_received_tracking_state_set = bet_api.get_latest_tracking_state_set()
63 print_latest_tracking_state_set(last_received_tracking_state_set, last_update_timestamp_sec)
64
65 # The SimGameCameraState held by the TrackingStateSet is a snapshot at the tracking
66 # rate. For an in-game camera consumer it is preferable to query
67 # bet_api.sample_smooth_sim_game_camera_state() from the render loop instead.
68 print_sim_game_camera_state(
69 bet_api, last_received_tracking_state_set.sim_game_camera_state()
70 )
71 count += 1
72
73
74if __name__ == "__main__":
75 main()
1/**
2 * Copyright (c) 2026, Eyeware Tech SA.
3 *
4 * All rights reserved.
5 */
6import {
7 createSession,
8 printLatestTrackingStateSet,
9 printSimGameCameraState,
10 printTrackingDataReceptionStatus,
11 printTrackingDataReceptionStatusIfChanged,
12} from "../beam_sample_utils.js";
13
14export async function runSyncDataAccessSample(log, shouldStop) {
15 const api = await createSession("Wait JavaScript Sample");
16 let previousStatus = api.getTrackingDataReceptionStatus();
17
18 log.write("Running wait data access sample.");
19 log.write("Browser JavaScript waits asynchronously so the page remains responsive.");
20 printTrackingDataReceptionStatus(api, log, previousStatus);
21
22 try {
23 const endTimeMs = performance.now() + 30_000;
24 while (!shouldStop() && performance.now() < endTimeMs) {
25 const status = api.getTrackingDataReceptionStatus();
26 printTrackingDataReceptionStatusIfChanged(api, log, previousStatus, status);
27 previousStatus = status;
28
29 if (await api.waitForNewTrackingStateSet(1000)) {
30 const trackingStateSet = api.getLastTrackingStateSet();
31 printLatestTrackingStateSet(log, trackingStateSet);
32 printSimGameCameraState(api, log, trackingStateSet.sim_game_camera_state);
33 }
34 }
35 } finally {
36 api.destroy();
37 }
38}
Polling data access¶
Polling is convenient in case your thread can’t be blocked, and it is not critical to read the
TrackingStateSet as soon as it is available, meaning, you can afford some latency. A typical scenario is when your thread loop is driven by other events and you want to use
the latest TrackingStateSet in combination with said events. For example, in a game, where
there is a logic driven by frames rendering: you don’t want to block, but you want to use the
eye tracking for some interaction in the game.
The mechanism works the same as Synchronous data access, with the key difference being that we
use the wait_for_new_tracking_state_set with a timeout of 0ms so that it returns immediately,
but indicating whether new data is available or not.
1/**
2 * Copyright (c) 2025, Eyeware Tech SA.
3 *
4 * All rights reserved.
5 *
6 * @brief This sample demonstrates how to use the polling data access method.
7 *
8 * The polling pattern is well suited for loops already driven by an external event (for example,
9 * a game's frame loop). Note that for an in-game camera consumer, the recommended pattern is to
10 * query @ref API::sample_smooth_sim_game_camera_state on each iteration of the consumer's loop,
11 * independently of whether a new TrackingStateSet has arrived. This sample interleaves both:
12 * polling TrackingStateSet for user state, HUD and foveated rendering data, and sampling the
13 * smoothed sim camera state on every iteration.
14 */
15#include "../bet_sample_utils.h"
16#include "eyeware/beam_eye_tracker.h"
17#include <chrono>
18#include <memory>
19#include <thread>
20
21int main() {
22
23 eyeware::beam_eye_tracker::API bet_api =
24 create_bet_api_or_exit("Polling Sample", eyeware::beam_eye_tracker::ViewportGeometry());
25
26 // We hold a timestamp instance useful to sync the data reception.
27 eyeware::beam_eye_tracker::Timestamp last_update_timestamp_sec{
28 eyeware::beam_eye_tracker::NULL_DATA_TIMESTAMP};
29
30 std::unique_ptr<eyeware::beam_eye_tracker::TrackingStateSet> last_received_tracking_state_set;
31
32 eyeware::beam_eye_tracker::TrackingDataReceptionStatus previous_status =
33 bet_api.get_tracking_data_reception_status();
34
35 print_tracking_data_reception_status(previous_status);
36
37 int count = 0;
38 // Access 1 minute of data (assuming a 10fps events)
39 while (count < 10 * 60) {
40
41 // Follow up with the status of the tracking data reception.
42 eyeware::beam_eye_tracker::TrackingDataReceptionStatus status =
43 bet_api.get_tracking_data_reception_status();
44 print_tracking_data_reception_status_if_changed(previous_status, status);
45 previous_status = status;
46
47 // Polling for new data follows the synchronous data access method, but with a timeout of
48 // 0ms. This means that the function will return immediately.
49 // Similarly, last_update_timestamp_sec is updated in case of new data.
50 if (bet_api.wait_for_new_tracking_state_set(last_update_timestamp_sec, 0)) {
51 // This is how we access the latest TrackingStateSet.
52 // It is only movable, thus this is a way to retain it.
53 last_received_tracking_state_set =
54 std::make_unique<eyeware::beam_eye_tracker::TrackingStateSet>(
55 std::move(bet_api.get_latest_tracking_state_set()));
56
57 print_latest_tracking_state_set(*last_received_tracking_state_set,
58 last_update_timestamp_sec);
59
60 count++;
61 }
62
63 // Sample the smoothed sim game camera state on every loop iteration, regardless of
64 // whether a new TrackingStateSet has arrived. This is the recommended access pattern
65 // for an in-game camera consumer.
66 const eyeware::beam_eye_tracker::SimGameCameraState sim_game_camera_state_for_render_frame =
67 bet_api.sample_smooth_sim_game_camera_state();
68 print_sim_game_camera_state(bet_api, sim_game_camera_state_for_render_frame);
69
70 // To simulate an "external event" driving this thread at 10fps, we sleep 100ms to simulate
71 // a thread loop driven by other events.
72 std::this_thread::sleep_for(std::chrono::milliseconds(100));
73 }
74
75 return 0;
76}
1# Copyright (c) 2025, Eyeware Tech SA.
2#
3# All rights reserved.
4#
5# This sample demonstrates how to use the polling data access method.
6#
7# The polling pattern is well suited for loops already driven by an external event (for example,
8# a game's frame loop). Note that for an in-game camera consumer, the recommended pattern is to
9# query API.sample_smooth_sim_game_camera_state on each iteration of the consumer's loop,
10# independently of whether a new TrackingStateSet has arrived. This sample interleaves both:
11# polling TrackingStateSet for user state, HUD and foveated rendering data, and sampling the
12# smoothed sim camera state on every iteration.
13import sys
14import os
15
16sys.path.append(os.path.join(os.path.dirname(__file__), ".."))
17
18import bet_add_to_python_path
19
20import time
21from eyeware import beam_eye_tracker
22from bet_sample_utils import (
23 create_bet_api_or_exit,
24 print_tracking_data_reception_status,
25 print_latest_tracking_state_set,
26 print_sim_game_camera_state,
27 print_tracking_data_reception_status_if_changed,
28)
29
30
31def main():
32 # Initialize API with empty viewport geometry
33 viewport_geometry = beam_eye_tracker.ViewportGeometry()
34 viewport_geometry.point_00 = beam_eye_tracker.Point(0, 0)
35 viewport_geometry.point_11 = beam_eye_tracker.Point(0, 0)
36
37 bet_api = create_bet_api_or_exit("Python Polling Sample", viewport_geometry)
38
39 # We hold a timestamp instance useful to sync the data reception
40 last_update_timestamp_sec = beam_eye_tracker.NULL_DATA_TIMESTAMP()
41
42 last_received_tracking_state_set = None
43 previous_status = bet_api.get_tracking_data_reception_status()
44 print_tracking_data_reception_status(previous_status)
45
46 count = 0
47 # Access 1 minute of data (assuming a 10fps events)
48 while count < 10 * 60:
49 # Follow up with the status of the tracking data reception
50 status = bet_api.get_tracking_data_reception_status()
51 print_tracking_data_reception_status_if_changed(previous_status, status)
52 previous_status = status
53
54 # Polling for new data follows the synchronous data access method, but with a timeout of
55 # 0ms. This means that the function will return immediately.
56 # Similarly, last_update_timestamp_sec is updated in case of new data.
57 if bet_api.wait_for_new_tracking_state_set(last_update_timestamp_sec, 0):
58 # This is how we access the latest TrackingStateSet
59 last_received_tracking_state_set = bet_api.get_latest_tracking_state_set()
60 print_latest_tracking_state_set(last_received_tracking_state_set, last_update_timestamp_sec)
61 count += 1
62
63 # Sample the smoothed sim game camera state on every loop iteration, regardless of
64 # whether a new TrackingStateSet has arrived. This is the recommended access pattern
65 # for an in-game camera consumer.
66 sim_game_camera_state_for_render_frame = bet_api.sample_smooth_sim_game_camera_state()
67 print_sim_game_camera_state(bet_api, sim_game_camera_state_for_render_frame)
68
69 # To simulate an "external event" driving this thread at 10fps, we sleep 100ms to simulate
70 # a thread loop driven by other events.
71 time.sleep(0.1)
72
73
74if __name__ == "__main__":
75 main()
1/**
2 * Copyright (c) 2026, Eyeware Tech SA.
3 *
4 * All rights reserved.
5 */
6import {
7 createSession,
8 delay,
9 printLatestTrackingStateSet,
10 printSimGameCameraState,
11 printTrackingDataReceptionStatus,
12 printTrackingDataReceptionStatusIfChanged,
13} from "../beam_sample_utils.js";
14
15export async function runPollingDataAccessSample(log, shouldStop) {
16 const api = await createSession("Polling JavaScript Sample");
17 let previousStatus = api.getTrackingDataReceptionStatus();
18
19 log.write("Running polling data access sample.");
20 printTrackingDataReceptionStatus(api, log, previousStatus);
21
22 try {
23 const endTimeMs = performance.now() + 60_000;
24 while (!shouldStop() && performance.now() < endTimeMs) {
25 const status = api.getTrackingDataReceptionStatus();
26 printTrackingDataReceptionStatusIfChanged(api, log, previousStatus, status);
27 previousStatus = status;
28
29 if (await api.waitForNewTrackingStateSet(0)) {
30 printLatestTrackingStateSet(log, api.getLastTrackingStateSet());
31 }
32
33 printSimGameCameraState(api, log, api.sampleSmoothSimGameCameraState());
34 await delay(100);
35 }
36 } finally {
37 api.destroy();
38 }
39}
Asynchronous data access¶
Asynchronous data access is convenient in case you want to get the latest TrackingStateSet updates at low
latency, and you do not want to block your thread while waiting for data updates.
It works by providing a callback mechanism to notify the client code when a new TrackingStateSet is available.
For OOP languages, like C++ and Python, this is done by inheriting from TrackingListener class and overriding
specific methods which behave as your callbacks. In JavaScript, this is done by passing an object with callback functions
to startReceivingTrackingData. In some cases you already have a higher level class managing the API instance and you simply
inherit TrackingListener or forward the JavaScript callbacks to the relevant methods.
In C, the registration of the callbacks is done by calling the EW_BET_API_RegisterTrackingCallbacks function. This function returns a handle to the callbacks, which must be used to unregister the callbacks by calling the EW_BET_API_UnregisterTrackingCallbacks function.
In OOP languages, the registration of the TrackingListener is done by calling the start_receiving_tracking_data_on_listener method which returns a handle. This handle must be used to unregister the callbacks by calling the stop_receiving_tracking_data_on_listener method.
In JavaScript, callbacks are stopped by calling stopReceivingTrackingData or by destroying the API instance.
1/**
2 * Copyright (c) 2025, Eyeware Tech SA.
3 *
4 * All rights reserved.
5 *
6 * @brief This sample demonstrates how to use the asynchronous data access method.
7 *
8 * The async listener delivers TrackingStateSet updates at the tracking rate. For consumers of
9 * the sim game camera output (e.g. a game's render thread), call
10 * @ref API::sample_smooth_sim_game_camera_state from the consumer's own loop instead — this
11 * sample shows the two side by side, with the listener handling user/HUD updates and a
12 * separate loop sampling the smoothed camera state at a higher cadence.
13 *
14 * @warning: the TrackingListener instance must be alive in between the
15 * start_receiving_tracking_data_on_listener and the stop_receiving_tracking_data_on_listener calls.
16 */
17#include "../bet_sample_utils.h"
18#include "eyeware/beam_eye_tracker.h"
19#include <chrono>
20#include <thread>
21
22class MyTrackingListener : public eyeware::beam_eye_tracker::TrackingListener {
23
24 public:
25 MyTrackingListener(eyeware::beam_eye_tracker::TrackingDataReceptionStatus status)
26 : m_previous_status(status) {
27 print_tracking_data_reception_status(status);
28 }
29
30 void on_tracking_state_set_update(
31 const eyeware::beam_eye_tracker::TrackingStateSet &tracking_state_set,
32 const eyeware::beam_eye_tracker::Timestamp timestamp) override {
33
34 print_latest_tracking_state_set(tracking_state_set, timestamp);
35 }
36
37 void on_tracking_data_reception_status_changed(
38 eyeware::beam_eye_tracker::TrackingDataReceptionStatus status) override {
39
40 print_tracking_data_reception_status_if_changed(m_previous_status, status);
41 m_previous_status = status;
42 }
43
44 private:
45 eyeware::beam_eye_tracker::TrackingDataReceptionStatus m_previous_status =
46 eyeware::beam_eye_tracker::TrackingDataReceptionStatus::NOT_RECEIVING_TRACKING_DATA;
47};
48
49int main() {
50 eyeware::beam_eye_tracker::API bet_api = create_bet_api_or_exit(
51 "Async Sample", eyeware::beam_eye_tracker::ViewportGeometry{0, 0, 0, 0});
52
53 // Create an instance of your custom listener, which is the class receiving updates.
54 MyTrackingListener async_listener(bet_api.get_tracking_data_reception_status());
55
56 // Start receiving tracking data.
57 eyeware::beam_eye_tracker::TRACKING_LISTENER_HANDLE listener_handle =
58 bet_api.start_receiving_tracking_data_on_listener(&async_listener);
59
60 // Sampling loop running at ~60 Hz, independent of the listener callback frequency. This
61 // mirrors what a game engine would do on its render thread.
62 const auto end_time = std::chrono::steady_clock::now() + std::chrono::seconds(30);
63 while (std::chrono::steady_clock::now() < end_time) {
64 const eyeware::beam_eye_tracker::SimGameCameraState sim_game_camera_state_for_render_frame =
65 bet_api.sample_smooth_sim_game_camera_state();
66 print_sim_game_camera_state(bet_api, sim_game_camera_state_for_render_frame);
67 std::this_thread::sleep_for(std::chrono::milliseconds(16));
68 }
69
70 bet_api.stop_receiving_tracking_data_on_listener(listener_handle);
71
72 return 0;
73}
1# Copyright (c) 2025, Eyeware Tech SA.
2#
3# All rights reserved.
4#
5# This sample demonstrates how to use the asynchronous data access method.
6#
7# The async listener delivers TrackingStateSet updates at the tracking rate. For consumers of
8# the sim game camera output (e.g. a game's render thread), call
9# API.sample_smooth_sim_game_camera_state from the consumer's own loop instead — this sample
10# shows the two side by side, with the listener handling user/HUD updates and a separate loop
11# sampling the smoothed camera state at a higher cadence.
12#
13# WARNING: the TrackingListener instance must be alive in between the
14# start_receiving_tracking_data_on_listener and the stop_receiving_tracking_data_on_listener calls.
15import sys
16import os
17
18sys.path.append(os.path.join(os.path.dirname(__file__), ".."))
19
20import bet_add_to_python_path
21
22import time
23from eyeware import beam_eye_tracker
24from bet_sample_utils import (
25 create_bet_api_or_exit,
26 print_tracking_data_reception_status,
27 print_latest_tracking_state_set,
28 print_sim_game_camera_state,
29 print_tracking_data_reception_status_if_changed,
30)
31
32
33class MyTrackingListener(beam_eye_tracker.TrackingListener):
34 def __init__(self, status: beam_eye_tracker.TrackingDataReceptionStatus):
35 super().__init__()
36 self.previous_status = status
37 print_tracking_data_reception_status(status)
38
39 def on_tracking_state_set_update(
40 self, tracking_state_set: beam_eye_tracker.TrackingStateSet, timestamp: float
41 ) -> None:
42 print_latest_tracking_state_set(tracking_state_set, timestamp)
43
44 def on_tracking_data_reception_status_changed(self, status: beam_eye_tracker.TrackingDataReceptionStatus) -> None:
45 print_tracking_data_reception_status_if_changed(self.previous_status, status)
46 self.previous_status = status
47
48
49def main():
50 # Initialize API with empty viewport geometry
51 viewport_geometry = beam_eye_tracker.ViewportGeometry()
52 viewport_geometry.point_00 = beam_eye_tracker.Point(0, 0)
53 viewport_geometry.point_11 = beam_eye_tracker.Point(0, 0)
54
55 bet_api = create_bet_api_or_exit("Python Async Sample", viewport_geometry)
56
57 # Create an instance of your custom listener, which is the class receiving updates
58 async_listener = MyTrackingListener(bet_api.get_tracking_data_reception_status())
59
60 # Start receiving tracking data
61 listener_handle = bet_api.start_receiving_tracking_data_on_listener(async_listener)
62
63 # Sampling loop running at ~60 Hz, independent of the listener callback frequency. This
64 # mirrors what a game engine would do on its render thread.
65 end_time = time.monotonic() + 30.0
66 while time.monotonic() < end_time:
67 sim_game_camera_state_for_render_frame = bet_api.sample_smooth_sim_game_camera_state()
68 print_sim_game_camera_state(bet_api, sim_game_camera_state_for_render_frame)
69 time.sleep(1.0 / 60.0)
70
71 bet_api.stop_receiving_tracking_data_on_listener(listener_handle)
72
73
74if __name__ == "__main__":
75 main()
1/**
2 * Copyright (c) 2026, Eyeware Tech SA.
3 *
4 * All rights reserved.
5 */
6import {
7 createSession,
8 delay,
9 printLatestTrackingStateSet,
10 printSimGameCameraState,
11 printTrackingDataReceptionStatus,
12 printTrackingDataReceptionStatusIfChanged,
13} from "../beam_sample_utils.js";
14
15export async function runAsyncDataAccessSample(log, shouldStop) {
16 const api = await createSession("Async JavaScript Sample");
17 let previousStatus = api.getTrackingDataReceptionStatus();
18
19 log.write("Running async data access sample.");
20 printTrackingDataReceptionStatus(api, log, previousStatus);
21
22 api.startReceivingTrackingData({
23 onStatusChanged(status) {
24 printTrackingDataReceptionStatusIfChanged(api, log, previousStatus, status);
25 previousStatus = status;
26 },
27 onTrackingStateSetUpdate(trackingStateSet) {
28 printLatestTrackingStateSet(log, trackingStateSet);
29 },
30 });
31
32 try {
33 const endTimeMs = performance.now() + 30_000;
34 while (!shouldStop() && performance.now() < endTimeMs) {
35 printSimGameCameraState(api, log, api.sampleSmoothSimGameCameraState());
36 await delay(16);
37 }
38 } finally {
39 api.destroy();
40 }
41}
Warning
Client code must ensure that the callbacks (or TrackingListener instance, if relevant) stay alive until the handle is explicitly deregistered.
Launching the Beam Eye Tracker from your game or application¶
Client code can use the API object to launch the Beam Eye Tracker application, start the webcam
and the tracking output directly from your game or application. This is a quality of life feature
so that your users can focus on your game or application.
Should you want to use option, we advice to:
Create an option in your “Settings” user interface allowing the user to opt-in or opt-out (e.g. an “Auto-start Beam Eye Tracker” toggle option);
Be mindful of the timing of when you call this method, as it needs to feel natural to the user. Keep in mind that the webcam will be started and you don’t want to surprise the user, such as when the game is loading. Instead, a good approach is to call this method once at the beginning of a gameplay loop, so that the Beam Eye Tracker starts to operate exactly when required for gameplay reasons.
If the Beam Eye Tracker can’t be found or is not well configured such that it cannot start tracking, then this won’t have any effect, and it is assumed to be up to the user to troubleshoot. In sandboxed environments, like browser JavaScript, there is no access to the desktop executable thus the call can only start the tracking when the Beam Eye Tracker application is already running.
Note
The user is also expected to have a setting option inside the Beam Eye Tracker application so that they can opt-out of these requests, and have further control over their setup.
// Assume api is a valid EW_BET_API_HANDLE
EW_BET_API_AttemptStartingTheBeamEyeTracker(api);
// Assume api is a valid eyeware::beam_eye_tracker::API instance
api.attempt_starting_the_beam_eye_tracker();
# Assume api is a valid eyeware.beam_eye_tracker.API instance
api.attempt_starting_the_beam_eye_tracker()
// Assume api is a valid Beam Eye Tracker JavaScript API instance.
api.attemptStartingTheBeamEyeTracker();