> ## Documentation Index
> Fetch the complete documentation index at: https://docs.streampixel.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Example app walkthrough

> Walk through the Streampixel SDK Example React app, including project structure, loading config, lifecycle wiring, and developer tools.

The [Streampixel SDK Example](https://github.com/infinity-void-metaverse/Streampixel-SDK-Example) is a React application that demonstrates a complete SDK integration. This page walks through its structure and key patterns.

## Project structure

```text theme={"dark"}
Streampixel-SDK-Example/
├── public/
│   ├── index.html            # HTML entry point
│   ├── favicon.png
│   └── manifest.json         # PWA manifest
├── src/
│   ├── index.js              # React entry point
│   ├── index.css             # All styling (548 lines)
│   └── components/
│       └── App.js            # Main application component (706 lines)
├── config-overrides.js       # Webpack polyfills for the SDK
├── package.json
└── package-lock.json
```

## Setup and running

<Steps>
  <Step title="Clone the repository">
    ```bash theme={"dark"}
    git clone https://github.com/infinity-void-metaverse/Streampixel-SDK-Example.git
    ```
  </Step>

  <Step title="Install dependencies">
    ```bash theme={"dark"}
    cd Streampixel-SDK-Example
    npm install
    ```
  </Step>

  <Step title="Start the dev server">
    ```bash theme={"dark"}
    npm start
    ```

    Open `http://localhost:3000/YOUR_PROJECT_ID` in your browser.
  </Step>
</Steps>

### URL parameters

The example reads configuration from the URL:

| Parameter    | Source                   | Description                         |
| ------------ | ------------------------ | ----------------------------------- |
| Project ID   | URL path (`/PROJECT_ID`) | Your Streampixel project identifier |
| `streamerId` | Query param              | Target a specific UE instance       |
| `sfuHost`    | Query param              | Set to `true` for SFU host mode     |
| `sfuPlayer`  | Query param              | Set to `true` for SFU viewer mode   |

Example URL: `http://localhost:3000/abc123?sfuPlayer=true&streamerId=host1`

## Feature toggles

At the top of `App.js`:

```javascript theme={"dark"}
const SHOW_DEV_TOOLS = true;  // Set to false to hide the Developer Tools panel
```

<Info>
  Set `SHOW_DEV_TOOLS` to `false` before deploying to production. The Developer Tools panel exposes console commands, JSON messaging, and mic/camera controls that are intended for development only.
</Info>

## LOADING\_CONFIG

<Tip>
  The `LOADING_CONFIG` object is the easiest way to customize the loading experience. Change text, colors, and status messages in one place without modifying any event handler logic.
</Tip>

The example centralizes all loading screen text and colors in a single configuration object:

```javascript theme={"dark"}
const LOADING_CONFIG = {
  backgroundColor: '#18181A',
  accentColor: '#4e9cff',
  logoUrl: null,                    // Path to a logo image
  title: 'Connecting to Stream',
  subtitle: 'Please wait while we set up your experience...',
  disconnectedSubtitle: 'The stream session has ended.',
  showSpinner: true,
  queueMessage: (position) => `You are in queue at position ${position}`,

  statusMessages: {
    initializing:     'Initializing...',
    connecting:       'Connecting to server...',
    webRtcConnecting: 'Establishing WebRTC connection...',
    sdpNegotiation:   'Negotiating stream parameters...',
    webRtcConnected:  'WebRTC connected, loading stream...',
    streamLoading:    'Stream is loading...',
    playingStream:    'Starting video playback...',
    inQueue:          'Waiting in queue...',
    failed:           'Connection failed. Please try again.',
    disconnected:     'Disconnected from stream.',
    reconnecting:     'Reconnecting to stream...',
    retrying:         'Retrying connection...',
    reconnected:      'Reconnected! Loading stream...',
    reconnectFailed:  'Unable to reconnect. Please refresh the page.',
  },

  reconnectingTitle:       'Reconnecting',
  reconnectingSubtitle:    'Please wait while we restore your session...',
  reconnectedTitle:        'Reconnected',
  reconnectFailedTitle:    'Reconnection Failed',
  reconnectFailedSubtitle: 'We were unable to restore your session.',
};
```

Modify this object to customize the loading experience without touching event handlers.

## How the example wires SDK events

### SDK initialization

The app initializes inside a `useEffect` hook that runs when `projectId` is available:

```javascript theme={"dark"}
useEffect(() => {
  if (!projectId) return;

  const startPlay = async () => {
    const { appStream, pixelStreaming, queueHandler, UIControl, reconnectStream } =
      await StreamPixelApplication({
        appId: projectId,
        AutoConnect: true,
        streamerId,
        sfuHost,
        sfuPlayer,
        forceTurn: true,
      });

    // Store refs for use in event handlers
    pixelStreamingRef.current = pixelStreaming;
    appStreamRef.current = appStream;
    uiControlRef.current = UIControl;

    // ... register events ...
  };

  startPlay();
}, [projectId, streamerId, sfuHost, sfuPlayer]);
```

### Lifecycle events to loading progress

Each WebRTC event updates React state for the loading screen:

```javascript theme={"dark"}
pixelStreaming.addEventListener('webRtcConnecting', () => {
  setLoadingStatus(LOADING_CONFIG.statusMessages.webRtcConnecting);
  setLoadingProgress(30);
});

// ... similar for each event ...

appStream.onVideoInitialized = () => {
  videoRef.current.append(appStream.rootElement);
  setLoadingProgress(100);
  setTimeout(() => setIsLoading(false), 300);
};
```

### Reconnection to loading screen

The reconnection state drives the loading screen back into view:

```javascript theme={"dark"}
reconnectStream.on('state', (data) => {
  switch (data.status) {
    case 'reconnecting':
      setIsLoading(true);
      setLoadingTitle(LOADING_CONFIG.reconnectingTitle);
      break;
    case 'connected':
      setLoadingTitle(LOADING_CONFIG.reconnectedTitle);
      break;
    case 'failed':
      setLoadingTitle(LOADING_CONFIG.reconnectFailedTitle);
      break;
  }
});
```

### AFK warning to custom overlay

The example replaces the default AFK overlay with a styled card:

```javascript theme={"dark"}
pixelStreaming.addEventListener('afkWarningActivate', (e) => {
  setAfkWarning(true);
  setAfkCountdown(e.data.countDown);
  dismissAfkRef.current = e.data.dismissAfk;
});

pixelStreaming.addEventListener('afkWarningUpdate', (e) => {
  setAfkCountdown(e.data.countDown);
});

pixelStreaming.addEventListener('afkWarningDeactivate', () => {
  setAfkWarning(false);
});
```

The dismiss button calls `dismissAfkRef.current()`.

## Developer Tools panel

When `SHOW_DEV_TOOLS` is `true`, the example shows a panel with these sections:

| Section                   | What it does                                                            |
| ------------------------- | ----------------------------------------------------------------------- |
| **Console Command**       | Sends UE console commands (e.g., `stat fps`) via `emitConsoleCommand()` |
| **UI Interaction (JSON)** | Sends custom JSON to UE via `emitUIInteraction()`                       |
| **Connection**            | Disconnect button via `pixelStreaming.disconnect()`                     |
| **Microphone & Camera**   | Enable mic/camera via `unmuteMicrophone()` / `unmuteCamera()`           |
| **Hovering Mouse**        | Toggle hover mouse via `UIControl.toggleHoveringMouse()`                |

## Stream controls toolbar

The bottom-right toolbar shows after loading completes:

* **Mute/Unmute** -- Toggles both video and audio elements
* **Fullscreen** -- Toggles browser fullscreen mode
* **Stream Info** -- Shows stats from `UIControl.getStreamStats()`
* **Settings** (if resolution enabled) -- Resolution selection dropdown
* **Dev Tools** (if enabled) -- Opens the Developer Tools panel

## Key React patterns used

* **`useRef`** for SDK instances (`pixelStreamingRef`, `appStreamRef`, `uiControlRef`) -- avoids re-renders
* **`useState`** for UI state (loading, controls, AFK, stats)
* **`useCallback`** for event handlers (mute, fullscreen, dev tools actions)
* **`mounted` flag** in useEffect to prevent state updates after unmount
* **Event handler `stopPropagation`** on dev tools inputs to prevent keyboard events from reaching the stream

## Next steps

<CardGroup cols={2}>
  <Card title="Custom loading screen" icon="spinner" href="/resources/web-sdk/ui-and-customization/custom-loading-screen">
    Build a loading screen driven by SDK lifecycle events.
  </Card>

  <Card title="Troubleshooting" icon="wrench" href="/resources/web-sdk/guides/troubleshooting">
    Resolve common SDK integration issues.
  </Card>
</CardGroup>
