# Events handlers

The Arcware Pixel Streaming WebSDK exposes several **event handlers** that allow your web application to react to changes in the streaming lifecycle.

These handlers can be used to implement:

* loading screens
* queue interfaces
* connection monitoring
* error handling
* session tracking
* analytics and debugging

Event handlers are exposed on the **PixelStreaming instance** and are available to both:

| Mode                             | Supported |
| -------------------------------- | --------- |
| ArcwareInit (UI integration)     | ✔         |
| CoreSetup (Headless integration) | ✔         |

Handlers are typically implemented using the `.add()` method.

Example:

```typescript
PixelStreaming.someHandler.add((event) => {
  console.log(event);
});
```

Multiple listeners can be attached to the same handler.

***

## VideoInitialized

Triggered when the video stream becomes available and the **first frame starts rendering**.

This event indicates that:

* the WebRTC connection is established
* the stream is active
* rendering has started

Typical uses:

* hide loading overlays
* start UI timers
* enable UI controls
* begin analytics tracking

Example:

```typescript
PixelStreaming.videoInitializedHandler.add(() => {
  console.log("Video initialized");
});
```

Example usage:

```typescript
PixelStreaming.videoInitializedHandler.add(() => {
  document.getElementById("loading-overlay").style.display = "none";
});
```

***

## queueHandler

Triggered when the user enters or moves within the **Arcware streaming queue**.

The queue is used when all streaming instances are currently occupied.

Instead of immediately launching a new instance, the user is placed in a queue until a streaming slot becomes available.

This handler allows applications to display **custom queue interfaces**.

Example:

```typescript
PixelStreaming.queueHandler.add((message) => {
  console.log("Queue update:", message);
});
```

Queue payload:

```typescript
export interface Queue {
  type: "queue";
  queue: {
    /** Position in queue (zero based). */
    index?: number;
    /** Length of the queue. */
    queueLength?: number;
    /** Already waited for. */
    waited?: number;
    /** Estimated wait time. */
    estimatedWaitTime?: number | null;
    /** Average wait time. */
    averageWaitTime?: number | null;
    /** Type of all time-values in the queue. */
    valueType: QueueValueType;
  };
}

export type QueueValueType =
  | "milliseconds"
  | "seconds"
  | "minutes"
  | "hours"
  | "days";
```

Meaning of the fields:

| Field                     | Type                          | Description                             |
| ------------------------- | ----------------------------- | --------------------------------------- |
| `type`                    | `"queue"`                     | Identifies the message as a queue event |
| `queue.index`             | `number \| undefined`         | Current position in queue, zero-based   |
| `queue.queueLength`       | `number \| undefined`         | Total queue length                      |
| `queue.waited`            | `number \| undefined`         | Time already waited                     |
| `queue.estimatedWaitTime` | `number \| null \| undefined` | Estimated remaining wait time           |
| `queue.averageWaitTime`   | `number \| null \| undefined` | Average wait time                       |
| `queue.valueType`         | `QueueValueType`              | Unit used for all time values           |

Example usage:

```typescript
PixelStreaming.queueHandler.add((message) => {
  const index = message.queue.index ?? 0;
  const queueLength = message.queue.queueLength ?? 0;
  const waited = message.queue.waited ?? 0;
  const unit = message.queue.valueType;

  document.getElementById("queue-position").innerText =
    `Position in queue: ${index + 1} / ${queueLength}`;

  document.getElementById("queue-waited").innerText =
    `Already waited: ${waited} ${unit}`;
});
```

Example UI:

```html
<div id="queue-overlay">
  <p id="queue-position"></p>
  <p id="queue-waited"></p>
</div>
```

Typical queue lifecycle:

````
User connects
      ↓
All instances occupied
      ↓
User enters queue
      ↓
Queue updates (position changes)
      ↓
Instance becomes available
      ↓
User leaves queue
      ↓
Stream starts
```<div data-gb-custom-block data-tag="hint" data-style='warning'>`estimatedWaitTime` and `averageWaitTime` are still experimental and should currently not be relied on for production-facing UI.</div>

---

# errorHandler

Triggered when an **error occurs during the streaming lifecycle**.

This includes errors originating from:

* WebRTC connection failures
* Pixel Streaming infrastructure errors
* Arcware backend messages

Example:

```typescript
PixelStreaming.errorHandler.add((error) => {
  console.error("Streaming error:", error);
});
````

Example usage:

```typescript
PixelStreaming.errorHandler.add((error) => {
  console.error("Streaming error:", error);
  alert("A streaming error occurred.");
});
```

***

## sessionIdHandler

Triggered when the streaming session receives a **session identifier**.

The session ID uniquely identifies the active streaming session.

This can be useful for:

* analytics
* logging
* reconnect workflows
* debugging

Example:

```typescript
PixelStreaming.sessionIdHandler.add((sessionId) => {
  console.log("Session ID:", sessionId);
});
```

Example usage:

```typescript
PixelStreaming.sessionIdHandler.add((sessionId) => {
  localStorage.setItem("streamSession", sessionId);
});
```

***

## loveLetterHandler

Triggered when the backend sends a **Love Letter** describing the current connection stage.

These messages are status updates sent by the backend while the stream is being prepared.

They are especially useful for:

* custom loading overlays
* connection progress messaging
* debugging startup issues

Example:

```typescript
PixelStreaming.loveLetterHandler.add((message) => {
  console.log("Love Letter:", message);
});
```

Love Letter payload:

```typescript
export interface LoveLetter {
  type: "letter";
  reason: string; // must start with "LL: "
  code: number;
  verbosity: number;
}
```

Meaning of the fields:

| Field       | Type       | Description                                                       |
| ----------- | ---------- | ----------------------------------------------------------------- |
| `type`      | `"letter"` | Identifies the message as a Love Letter                           |
| `reason`    | `string`   | Human-readable status message. Starts with `"LL: "`               |
| `code`      | `number`   | Numeric code representing the current loading or connection state |
| `verbosity` | `number`   | Indicates how verbose or detailed the message is                  |

Example usage:

```typescript
PixelStreaming.loveLetterHandler.add((message) => {
  document.getElementById("loading-message").innerText = message.reason;
});
```

Example UI:

```html
<div id="loading-overlay">
  <p id="loading-message">Starting stream...</p>
</div>
```

These messages are commonly used to implement **custom loading screens** or connection progress indicators.

***

## applicationResponseHandler

Triggered when Unreal Engine sends a **response message to the browser**.

This is the main mechanism used for **bidirectional communication between the web UI and Unreal Engine**.

Example:

```typescript
PixelStreaming.applicationResponseHandler = (response) => {
  console.log("Response from Unreal:", response);
};
```

Responses are typically strings but often contain serialized JSON.

Example:

```typescript
PixelStreaming.applicationResponseHandler = (response) => {
  const data = JSON.parse(response);

  if (data.type === "scoreUpdate") {
    document.getElementById("score").innerText = data.value;
  }
};
```

Example UI:

```html
<div>
  Score: <span id="score">0</span>
</div>
```

***

## websocketOnCloseHandler

Triggered when the **WebSocket connection to the signalling server is closed**.

This event indicates that the streaming session has ended or the connection has been interrupted.

Possible reasons include:

* the user disconnected
* the instance stopped
* network connectivity issues
* backend session termination

Example:

```typescript
PixelStreaming.websocketOnCloseHandler.add((event) => {
  console.log("WebSocket closed:", event);
});
```

Typical usage:

```typescript
PixelStreaming.websocketOnCloseHandler.add(() => {
  document.getElementById("connection-status").innerText =
    "Connection closed";
});
```

This handler can be used to:

* display reconnect options
* redirect the user
* show a connection lost screen
* trigger cleanup logic

***

## whiteLabellingChangedHandler

Triggered whenever **white-labelling configuration is applied or updated**.

White-labelling may be applied from different sources:

* SDK configuration
* backend-provided branding
* URL-based configuration (`?wl`)
* runtime updates

Example:

```typescript
PixelStreaming.whiteLabellingChangedHandler.add((whiteLabel) => {
  console.log("White labelling updated:", whiteLabel);
});
```

Typical usage:

```typescript
PixelStreaming.whiteLabellingChangedHandler.add((whiteLabel) => {
  if (whiteLabel.splashScreenBgColor) {
    document.body.style.backgroundColor =
      whiteLabel.splashScreenBgColor;
  }
});
```

This handler allows applications to react when branding settings change dynamically.

***

## postInitSideEffectsHandler

Triggered after the WebSDK has completed its **initial setup and post-initialization tasks**.

This event occurs after the SDK has finished applying configuration and preparing the streaming environment.

Example:

```typescript
PixelStreaming.postInitSideEffectsHandler.add(() => {
  console.log("Post initialization completed");
});
```

Typical usage:

```typescript
PixelStreaming.postInitSideEffectsHandler.add(() => {
  console.log("SDK fully initialized");
});
```

This handler is useful for:

* initializing UI elements that depend on the SDK
* starting custom analytics tracking
* running post-initialization logic

***

## fileTransferHandler

Triggered when **Unreal Engine sends a file to the browser through the Pixel Streaming data channel**.

This feature allows Unreal Engine to transfer files directly to the client.

Typical use cases include:

* screenshot downloads
* exporting generated assets
* sending generated reports or data files

Example:

```typescript
PixelStreaming.fileTransferHandler.add((file) => {
  console.log("Received file:", file);
});
```

The received file information can be accessed through the WebSDK.

Core method:

```typescript
PixelStreaming.getIncomingFile()
```

The returned object typically contains:

```typescript
{
  data: Uint8Array[],
  mimetype: string,
  extension: string,
  filename?: string
}
```

#### UI Integration Behavior

When using the **UI integration (`ArcwareInit`)**, the WebSDK automatically downloads incoming files for the user.

This means that files sent from Unreal Engine will appear as normal browser downloads without additional implementation.

#### Core Integration Behavior

When using **CoreSetup**, file handling must be implemented manually.

Core users can retrieve the incoming file and decide how to handle it.

Example: manual download

```typescript
PixelStreaming.fileTransferHandler.add(() => {
  const file = PixelStreaming.getIncomingFile();

  const blob = new Blob(file.data, {
    type: file.mimetype
  });

  const url = URL.createObjectURL(blob);

  const a = document.createElement("a");
  a.href = url;
  a.download = file.filename ?? `download${file.extension}`;

  document.body.appendChild(a);
  a.click();

  document.body.removeChild(a);
  URL.revokeObjectURL(url);
});
```

Core integrations can also use the built-in download helper to mirror the same behavior as the default UI integration:

```typescript
PixelStreaming.fileTransferHandler.add(() => {
  PixelStreaming.fileDownload();
});
```

### Fully Custom File Handling

Core integrations are not limited to browser downloads.

Applications can:

* parse the file contents
* open custom dialogs
* display previews
* upload files to external servers
* store files locally
* process them programmatically

Example: uploading to an API endpoint

```typescript
PixelStreaming.fileTransferHandler.add(async () => {
  const file = PixelStreaming.getIncomingFile();

  const blob = new Blob(file.data, {
    type: file.mimetype
  });

  const formData = new FormData();
  formData.append("file", blob, file.filename ?? "upload");

  await fetch("/api/upload", {
    method: "POST",
    body: formData
  });
});
```

This allows the WebSDK to integrate with workflows such as:

* asset pipelines
* data exports
* automated reporting
* cloud storage uploads.

***

## Using Multiple Handlers

Multiple handlers can be attached to the same event.

Example:

```typescript
PixelStreaming.videoInitializedHandler.add(() => {
  console.log("Stream ready");
});

PixelStreaming.videoInitializedHandler.add(() => {
  startAnalyticsTracking();
});
```

All registered handlers will be executed when the event occurs.

***

## Typical Event Lifecycle

The following illustrates the typical order of events when starting a stream:

```
Connection initiated
      ↓
loveLetterHandler
      ↓
queueHandler (if queue is active)
      ↓
sessionIdHandler
      ↓
videoInitializedHandler
      ↓
Stream ready
```

During the session:

```
User interacts
      ↓
emitUIInteraction
      ↓
Unreal processes event
      ↓
applicationResponseHandler
```

These handlers allow developers to build **fully custom streaming interfaces and control logic** around the WebSDK.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.arcware.cloud/web-integration/new-websdk/in-depth/events-handlers.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
