> ## 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.

# Performance tuning

> Hit your latency, FPS, and bitrate targets across regions and devices.

The outcome: a stream that hits the latency, FPS, and visual quality your use case requires, with the right tradeoffs for your audience's devices and networks.

Streaming performance is multidimensional — encode time on the worker, network RTT to the viewer, decode time on the client. Tuning means picking the right point on the curve, not maximizing one axis.

## Targets by use case

There is no universal "good performance." Pick the profile that matches your project:

| Use case                              | Glass-to-glass latency   | FPS   | Resolution        | Bitrate    |
| ------------------------------------- | ------------------------ | ----- | ----------------- | ---------- |
| Cinematic walkthrough                 | ≤ 200 ms                 | 30    | 1080p             | 6–10 Mbps  |
| Product configurator                  | ≤ 150 ms                 | 60    | 1080p             | 6–10 Mbps  |
| Multiplayer game (host + viewers)     | ≤ 120 ms                 | 60    | 1080p             | 4–6 Mbps   |
| Interactive simulation / sim training | ≤ 100 ms                 | 60    | 1440p             | 12–18 Mbps |
| VR                                    | ≤ 80 ms motion-to-photon | 72/90 | 1920×1080 per eye | 20–30 Mbps |

Glass-to-glass latency = camera move on the worker → photons on the viewer's display. Below 100 ms feels native; 100–200 ms is fine for non-twitch content; above 300 ms feels like you're operating a remote machine.

## Region selection

The single biggest lever. Streampixel runs in three regions:

* US-East-1
* Europe
* Asia Pacific

A user in Tokyo connecting to US-East-1 will see \~150 ms RTT before any encoding/decoding even starts. The same user connecting to Asia Pacific sees \~10–30 ms.

Pick the region closest to where most of your users are. If your audience spans continents, run the same project in multiple regions and route users at your application layer (geolocate IP, send them to the right URL).

See [Regions](/resources/concepts/regions) for the full list.

## Codec choice

| Codec                 | Quality / bitrate                      | Decode support                                         | When to use                                                 |
| --------------------- | -------------------------------------- | ------------------------------------------------------ | ----------------------------------------------------------- |
| **AV1** ★ Recommended | Best                                   | Chrome, Edge — modern hardware                         | Recommended primary codec for every use case                |
| VP9                   | \~40% better than H264 at same bitrate | Most modern browsers, software-decoded on many mobiles | Strong fallback for ArchViz / Digital Twin                  |
| H264                  | Baseline                               | Universal — every browser, every mobile, every headset | Universal fallback; required for Safari, iOS, older devices |
| VP8                   | Slightly worse than H264               | Wide                                                   | Rare; prefer H264                                           |

Set the preferred codec from the dashboard ([Codec settings](/resources/quick-start-guide/codec-settings)) or per-session in the SDK. **Default to AV1 primary** with a fallback that fits your audience.

<Tip>
  AV1 needs hardware decode on the client to win on latency. On older devices or browsers without AV1 support, the SDK falls back automatically — that's why every project should set a fallback codec (H264 for broad reach, VP9 for desktop-first ArchViz audiences).
</Tip>

## Resolution and bitrate

Pair these as a unit — high resolution at low bitrate looks worse than lower resolution at the same bitrate.

| Use case             | Resolution        | Bitrate    |
| -------------------- | ----------------- | ---------- |
| Mobile               | 720p              | 2–4 Mbps   |
| Desktop standard     | 1080p             | 6–10 Mbps  |
| Desktop high quality | 1440p             | 12–18 Mbps |
| Desktop 4K (rare)    | 2160p             | 25–40 Mbps |
| VR                   | 1920×1080 per eye | 20–30 Mbps |

For ready-made bitrate / QP presets by application type (ArchViz / Digital Twin, Chatbot, Games), see [Recommended settings by use case](/resources/quick-start-guide/resolution-settings#recommended-settings-by-use-case).

Two rules of thumb:

1. **Bits per pixel per second.** A 1080p60 stream at 6 Mbps gives \~50 milli-bits per pixel per frame. Below \~30 mbpp/f you start seeing compression artifacts in motion. Use this when sizing custom resolutions.
2. **Decode budget.** A device that can play your stream at 1080p smoothly may stutter at 1440p even if the network has the bandwidth. Decoder throughput is the hidden ceiling.

## Adaptive vs fixed bitrate

**Adaptive** (default) lets the encoder ramp bitrate up and down based on observed network capacity. Best for variable networks (mobile, home Wi-Fi).

**Fixed** holds a constant bitrate. Best for controlled environments (events on dedicated networks, kiosks on Ethernet) where you want predictable visual quality.

Tradeoffs:

|                          | Adaptive                    | Fixed                 |
| ------------------------ | --------------------------- | --------------------- |
| Quality on bad networks  | Drops bitrate, stays smooth | Drops frames, freezes |
| Quality on good networks | Climbs to ceiling           | Held constant         |
| Predictable bandwidth    | No                          | Yes                   |
| Best for                 | Variable / mobile           | Kiosks / events       |

Most production deployments should use adaptive. Switch to fixed when you have metrics showing the adaptive controller is making bad decisions (e.g., oscillating in a stable network).

## Reducing perceived latency

If your stream feels laggy even though throughput is fine, the bottleneck is somewhere other than bandwidth. Try, in order:

<Steps>
  <Step title="Check actual RTT in the stats panel">
    Open the running stream and press <kbd>T</kbd> + <kbd>5</kbd> together to open the stats panel. Read **RTT** directly from there — that's the real WebRTC round-trip, not a ping to the API. Anything above \~100 ms means the viewer is far from the worker; pick a closer region.
  </Step>

  <Step title="Match codec to client capability">
    On devices with AV1 hardware decode (modern Chrome / Edge desktops, recent Intel / AMD / Apple chips), AV1 is fastest *and* highest quality. On older devices or Safari / iOS, fall back to H264 — software-decoded VP9 / AV1 will add 10–30 ms on weaker hardware.
  </Step>

  <Step title="Lower minQP">
    Lowering the minimum quantization parameter lets the encoder spend more bits on each frame, reducing the chance of B-frame queueing. Configure from the dashboard's adaptive settings.
  </Step>

  <Step title="Raise maxBitrate">
    If you've capped maxBitrate aggressively for bandwidth reasons but the network can handle more, raising the cap reduces compression artifacts and the perception of lag.
  </Step>

  <Step title="Open UDP 10000–60000 outbound (optional)">
    On viewer networks where these ports are open, the SDK can establish a direct peer-to-peer connection to the rendering node instead of going through TURN — one fewer hop, lower latency. See [Network requirements](/resources/concepts/network-requirements#about-the-optional-udp-10000-60000-range).
  </Step>

  <Step title="Disable forceTurn (only on trusted networks)">
    `forceTurn: true` adds a relay hop. On a network you control where direct WebRTC works, removing it shaves a few ms. On any consumer network, leave it on — failed direct connections cost much more than the relay's overhead.
  </Step>
</Steps>

## Network: TURN-only mode for restrictive networks

Corporate networks often block UDP and direct WebRTC entirely. Set:

```javascript theme={"dark"}
StreamPixelApplication({
  appId: PROJECT_ID,
  forceTurn: true,
});
```

This forces all media through Streampixel's TURN servers, which support TCP fallback. The cost is a few ms of extra latency and slightly more relay load. The benefit is connections that simply work behind hotel Wi-Fi, hospital networks, and enterprise firewalls.

If you find yourself supporting many users on locked-down networks, leave `forceTurn` on by default and only disable it for performance-sensitive internal use cases.

## Diagnosing performance: stream stats

Subscribe to the SDK's stats events to see what's actually happening:

```javascript theme={"dark"}
pixelStreaming.addEventListener('statsReceived', (e) => {
  const stats = e.data.aggregatedStats;
  const inbound = stats.inboundVideoStats;
  const codec = stats.codecs.get(inbound.codecId);

  console.log({
    codec: codec?.mimeType,
    fps: inbound.framesPerSecond,
    bitrate: inbound.bitrate,
    resolution: `${inbound.frameWidth}x${inbound.frameHeight}`,
    packetsLost: inbound.packetsLost,
    rtt: stats.candidatePair?.currentRoundTripTime,
    jitter: inbound.jitter,
  });
});
```

What to look for:

| Symptom                           | Likely cause                                                             |
| --------------------------------- | ------------------------------------------------------------------------ |
| FPS oscillating between 30 and 60 | Decoder throttling (often thermal on mobile) or bitrate adaptation churn |
| Bitrate well below configured max | Network can't sustain higher; check RTT and packet loss                  |
| RTT > 100 ms                      | Wrong region, or user on bad network                                     |
| Packets lost > 1%                 | Network congestion or Wi-Fi interference                                 |
| Jitter > 30 ms                    | Unstable connection, likely Wi-Fi                                        |
| Codec not what you expected       | Browser negotiated a different codec — check `preferredCodec`            |

Render these in a dev overlay during testing. The stream stats panel in the [example app](/resources/web-sdk/guides/example-app-walkthrough) shows one approach.

## UE-side optimizations

Streampixel can't make a slow Unreal project fast. If your scene drops below 60 fps on the worker, no amount of network tuning will produce a 60 fps stream.

Quick wins that often help:

* **Lock the target framerate.** `t.MaxFPS 60` keeps the encoder in sync with consistent frame intervals. Variable frame times confuse the encoder.
* **Disable expensive shadows where not needed.** Movable lights with dynamic shadows are the most common framerate killer in real-time scenes.
* **Lower texture mip levels.** Streamed pixels won't show your highest-mip detail anyway. `r.Streaming.MipBias 1` saves VRAM with negligible visual impact at typical streaming resolutions.
* **Forward renderer for VR / mobile-targeted streams.** Cheaper, more deterministic frame times than the deferred renderer.
* **Disable AA passes that don't survive video encoding.** Heavy TAA gets blurred away by H264 anyway; FXAA or no AA can look identical and render faster.

These are general UE perf tips, not Streampixel-specific. Profile with Unreal Insights to find your actual bottleneck before tuning blindly.

## Debugging high latency

When users report "the stream feels laggy," walk this checklist:

<Steps>
  <Step title="Confirm region">
    Which region is the project in? Where is the user? If they don't match, fix that first.
  </Step>

  <Step title="Check stats">
    Ask the user to press <kbd>T</kbd> + <kbd>5</kbd> on the stream to open the stats panel and share RTT, Packet Loss, FPS, and bitrate. Stats > intuition.
  </Step>

  <Step title="Check codec">
    If they're on Safari or iOS, AV1 / VP9 won't decode — make sure your fallback codec is H264. On older Android devices, software-decoded VP9 / AV1 will also drag latency down.
  </Step>

  <Step title="Check network type">
    Cellular vs. Wi-Fi vs. Ethernet. Cellular adds 30–80 ms vs. wired. Wi-Fi 5 GHz is much better than 2.4 GHz for streaming.
  </Step>

  <Step title="Check forceTurn">
    If they're on a corporate network and `forceTurn: false`, the connection might have failed over to slower paths. Try with `forceTurn: true`.
  </Step>

  <Step title="Check UE framerate">
    If the worker is rendering at 30 fps because the scene is heavy, no amount of network tuning fixes that. Profile UE.
  </Step>
</Steps>

## Pre-launch perf checklist

Before opening to a real audience:

* [ ] Region matches audience geography.
* [ ] Codec set explicitly — **AV1 primary** with an appropriate fallback (H264 for broad reach, VP9 for desktop ArchViz).
* [ ] Resolution + bitrate pair appropriate for target devices. Start from the [recommended use-case presets](/resources/quick-start-guide/resolution-settings#recommended-settings-by-use-case).
* [ ] Adaptive bitrate enabled (unless you have a specific reason for fixed).
* [ ] `forceTurn: true` for broad audiences.
* [ ] UE project hits target FPS on the worker hardware.
* [ ] Stream stats panel available in dev/staging for spot-checks.
* [ ] Tested on the slowest device class your users will bring (e.g., a mid-range Android on LTE).

## Next steps

<CardGroup cols={2}>
  <Card title="Regions" icon="globe" href="/resources/concepts/regions">
    Where your project runs determines your latency floor.
  </Card>

  <Card title="Codec settings" icon="film" href="/resources/quick-start-guide/codec-settings">
    Choose H264, VP8, VP9, or AV1 per project.
  </Card>

  <Card title="Mobile streaming" icon="mobile" href="/resources/recipes/mobile-streaming">
    Bitrate and codec recipes specific to mobile browsers.
  </Card>

  <Card title="Multiplayer streaming" icon="users" href="/resources/recipes/multiplayer-streaming">
    Per-viewer bitrate budgets for SFU sessions.
  </Card>
</CardGroup>
