# WiFi DensePose Latency and FPS: Real-Time Performance Metrics Explained

> Discover WiFi DensePose latency and FPS performance metrics. Learn about real-time pose estimation throughput and stream data rates for the ruvnet/wifi-densepose repository.

- Repository: [rUv/wifi-densepose](https://github.com/ruvnet/wifi-densepose)
- Tags: performance
- Published: 2026-02-16

---

**WiFi DensePose streams pose estimation data at a default 30 FPS with average latency under 100 ms, while the underlying model maintains inference throughput above 10 FPS as validated by integration tests in the ruvnet/wifi-densepose repository.**

WiFi DensePose enables real-time human pose estimation using WiFi channel state information (CSI) transmitted over WebSocket connections. Understanding the latency and FPS characteristics is critical for deploying this open-source system in time-sensitive applications. This article examines the exact performance metrics defined in the codebase, how they are measured, and where to monitor them in production.

## Default Streaming Performance Metrics

WiFi DensePose separates **streaming FPS** (the rate at which pose data is broadcast to clients) from **inference FPS** (the speed of the neural network processing).

### Streaming FPS Configuration

The default streaming frequency is defined in the Pydantic settings model. In [`v1/src/config/settings.py`](https://github.com/ruvnet/wifi-densepose/blob/main/v1/src/config/settings.py), the `stream_fps` field controls how often the WebSocket server emits new pose frames:

```python

# v1/src/config/settings.py

stream_fps: int = Field(default=30, description="Streaming frames per second")

```

This 30 FPS default translates to a frame interval of approximately 33.3 ms, calculated as `1 / stream_fps`.

### Latency Benchmarks

Latency is measured as the round-trip time between receiving CSI data, running the pose model, and broadcasting the result. The integration test suite in [`v1/tests/integration/test_streaming_pipeline.py`](https://github.com/ruvnet/wifi-densepose/blob/main/v1/tests/integration/test_streaming_pipeline.py) enforces strict performance boundaries:

```python

# v1/tests/integration/test_streaming_pipeline.py

assert latency > 0
assert latency < 200            # per-frame must be < 200 ms

assert avg_latency < 100        # average < 100 ms

```

These assertions confirm that WiFi DensePose maintains **per-frame latency under 200 ms** and **average latency under 100 ms** under test conditions.

## How Performance Is Measured in the Codebase

The system tracks performance through three architectural layers: configuration, runtime statistics, and WebSocket timing control.

### Configuration Layer

The `Settings` class in [`v1/src/config/settings.py`](https://github.com/ruvnet/wifi-densepose/blob/main/v1/src/config/settings.py) serves as the single source of truth for streaming parameters. Beyond the default 30 FPS, it allows runtime overrides through environment variables or query parameters.

### Runtime Statistics

The `StreamService` class in [`v1/src/services/stream_service.py`](https://github.com/ruvnet/wifi-densepose/blob/main/v1/src/services/stream_service.py) maintains a live statistics dictionary that tracks latency and throughput:

```python

# v1/src/services/stream_service.py

self.stats = {
    "active_connections": 0,
    "total_connections": 0,
    "messages_sent": 0,
    "messages_failed": 0,
    "data_points_streamed": 0,
    "average_latency_ms": 0.0
}

```

The `average_latency_ms` value updates dynamically after each frame broadcast, providing real-time visibility into system performance.

### WebSocket Timing Control

The actual frame emission timing is controlled in [`v1/src/api/websocket/pose_stream.py`](https://github.com/ruvnet/wifi-densepose/blob/main/v1/src/api/websocket/pose_stream.py). The WebSocket loop uses an asynchronous sleep interval calculated from the configured FPS:

```python

# v1/src/api/websocket/pose_stream.py

await asyncio.sleep(1.0 / self.stream_config["fps"])

```

This ensures that the server respects the `stream_fps` setting regardless of inference speed, buffering or skipping frames as necessary to maintain the target broadcast rate.

## Inference Throughput vs. Streaming FPS

It is important to distinguish between the **streaming FPS** (WebSocket broadcast rate) and the **inference FPS** (model processing speed).

### Model Inference Speed

The neural network processing speed depends on hardware and model complexity. The performance test suite in [`v1/tests/performance/test_inference_speed.py`](https://github.com/ruvnet/wifi-densepose/blob/main/v1/tests/performance/test_inference_speed.py) validates the minimum throughput:

```python

# v1/tests/performance/test_inference_speed.py

assert stats["throughput_fps"] > 10      # at least 10 fps overall

```

Additionally, the benchmark verifies that lightweight model configurations can achieve:

```python

# v1/tests/performance/test_inference_speed.py

assert result["inference_time_ms"] < 50  # lightweight model < 50 ms

```

This translates to approximately 20 FPS inference capability for optimized models.

### Performance Test Validation

The integration tests ensure that the system gracefully handles the mismatch between inference and streaming speeds. When inference is slower than the streaming FPS, the system buffers or interpolates frames to maintain the WebSocket broadcast rate without breaking the latency constraints.

## Monitoring Real-Time Metrics

You can access current performance data through both programmatic APIs and HTTP endpoints.

### Retrieve Current Streaming FPS

```python
from src.config.settings import get_settings

settings = get_settings()
print(f"Current streaming FPS: {settings.stream_fps}")

```

### Access Runtime Latency Statistics

```python
from src.services.stream_service import StreamService
from src.config.settings import get_settings
from src.config.domains import DomainConfig

service = StreamService(get_settings(), DomainConfig())

# … after the service has processed a few frames …

print(f"Average latency (ms): {service.stats['average_latency_ms']}")

```

### Query Metrics via HTTP

```bash
curl http://localhost:8000/api/v1/metrics

```

The JSON response includes:

```json
{
  "stream_service": {
    "average_latency_ms": 84.2,
    "stream_fps": 30,
    "data_points_streamed": 1200
  }
}

```

### Override FPS for Specific Clients

You can adjust the streaming rate per WebSocket connection using query parameters:

```python

# WebSocket URL – set max_fps query param (max 60)

ws://localhost:8000/api/v1/stream/pose?zone_ids=zone_1&max_fps=45

```

## Summary

- **Default streaming FPS** is 30, configurable via [`v1/src/config/settings.py`](https://github.com/ruvnet/wifi-densepose/blob/main/v1/src/config/settings.py) or WebSocket query parameters.
- **Average latency** stays under 100 ms with a per-frame maximum of 200 ms, enforced by integration tests in [`v1/tests/integration/test_streaming_pipeline.py`](https://github.com/ruvnet/wifi-densepose/blob/main/v1/tests/integration/test_streaming_pipeline.py).
- **Inference throughput** exceeds 10 FPS independently of the streaming rate, with lightweight models capable of 20 FPS (50 ms per inference).
- **Real-time monitoring** is available through `StreamService.stats` and the `/api/v1/metrics` HTTP endpoint.

## Frequently Asked Questions

### What is the default FPS of WiFi DensePose?

The default streaming FPS is **30**, defined in the `stream_fps` field within [`v1/src/config/settings.py`](https://github.com/ruvnet/wifi-densepose/blob/main/v1/src/config/settings.py). This value controls how frequently the WebSocket server broadcasts pose estimation frames to connected clients, translating to a frame interval of approximately 33.3 milliseconds.

### How does WiFi DensePose measure latency?

Latency is calculated as the round-trip time between receiving CSI data, executing the pose estimation model, and broadcasting the result via WebSocket. The `StreamService` class in [`v1/src/services/stream_service.py`](https://github.com/ruvnet/wifi-densepose/blob/main/v1/src/services/stream_service.py) tracks this in the `stats["average_latency_ms"]` dictionary field, which updates dynamically after each frame transmission.

### Can the streaming FPS be adjusted dynamically?

Yes, the streaming FPS can be overridden at runtime. Clients can specify the `max_fps` query parameter when establishing a WebSocket connection (e.g., `ws://localhost:8000/api/v1/stream/pose?max_fps=45`), or developers can modify the `stream_fps` value in the `Settings` class before service initialization.

### What is the difference between streaming FPS and inference FPS?

**Streaming FPS** refers to the rate at which pose data is broadcast to clients via WebSocket (default 30 FPS), controlled by [`v1/src/api/websocket/pose_stream.py`](https://github.com/ruvnet/wifi-densepose/blob/main/v1/src/api/websocket/pose_stream.py). **Inference FPS** refers to the neural network processing speed, which the performance tests in [`v1/tests/performance/test_inference_speed.py`](https://github.com/ruvnet/wifi-densepose/blob/main/v1/tests/performance/test_inference_speed.py) validate at >10 FPS (up to 20 FPS for lightweight models). The system buffers or interpolates frames when inference speed differs from the streaming rate.