Protocol Comparison
Choose the right streaming protocol for your use case.
Compare Live
Open Comparison Client — See all protocols side-by-side in real-time!
Quick Comparison
| Feature | WebTransport | NATS WS | WebSocket | SSE | REST API |
|---|---|---|---|---|---|
| Direction | Bidirectional | Bidirectional | Bidirectional | Server → Client | Request/Response |
| Protocol | UDP (QUIC) | WebSocket | TCP | HTTP | HTTP |
| Latency | 0.5-2ms | 0.5-2ms | 1-5ms | 10-50ms | 50-200ms |
| Overhead | 2-6 bytes | ~20 bytes | 2-14 bytes | ~50 bytes | ~200 bytes |
| Reconnection | Manual | Configurable | Manual | Automatic | N/A |
| Multiple streams | Yes | Via subjects | No | No | N/A |
| Wildcards | No | Yes | No | No | No |
| Direct broker | No | Yes | No | No | No |
| Browser support | Chrome/Firefox | ES Modules | Universal | IE except | Universal |
| Proxy-friendly | Rarely | Sometimes | Sometimes | Always | Always |
Detailed Analysis
Connection Establishment
Message Overhead
| Protocol | Frame Format | Overhead |
|---|---|---|
| WebTransport | [Length] + Payload | 2-6 bytes |
| NATS WS | [Subject][Payload] | ~20 bytes |
| WebSocket | [FIN][RSV][Opcode][Mask][Len] + Payload | 2-14 bytes |
| SSE | event: message\ndata: {...}\n\n | ~50 bytes |
| REST API | HTTP Headers + Body | ~200 bytes |
Use Case Matrix
| Use Case | Recommended | Why |
|---|---|---|
| Dashboard | SSE | Auto-reconnect, simple |
| Chat | WebSocket | Bidirectional needed |
| Gaming | WebTransport/NATS | Ultra-low latency |
| Notifications | SSE | Server push only |
| Trading | WebTransport/NATS | Low latency critical |
| Live scores | SSE | Simple, reliable |
| Multi-game monitoring | NATS | Wildcard subscriptions |
| Collaborative editing | WebSocket | Bidirectional, reliable |
| Video streaming | WebTransport | Unreliable OK, low latency |
| History queries | REST API | Request/response, caching |
| Polling fallback | REST API | Universal compatibility |
Decision Tree
Performance Benchmarks
Latency (ms)
| Protocol | P50 | P95 | P99 |
|---|---|---|---|
| WebTransport | 0.8 | 1.5 | 2.4 |
| NATS WS | 0.9 | 1.8 | 2.8 |
| WebSocket | 2.1 | 4.8 | 8.2 |
| SSE | 15.3 | 38.2 | 62.1 |
| REST API | 65.0 | 120.0 | 180.0 |
Throughput (messages/second)
| Protocol | Single Connection | 1000 Connections |
|---|---|---|
| WebTransport | 80,000 | 75,000 |
| NATS WS | 70,000 | 65,000 |
| WebSocket | 50,000 | 45,000 |
| SSE | 20,000 | 18,000 |
| REST API | 100,000 | 90,000 |
Memory Usage (per connection)
| Protocol | Idle | Active |
|---|---|---|
| SSE | 4 KB | 8 KB |
| WebSocket | 8 KB | 16 KB |
| NATS WS | 10 KB | 18 KB |
| WebTransport | 12 KB | 20 KB |
Browser Compatibility
WebSocket
| Browser | Version | Support |
|---|---|---|
| Chrome | 4+ | ✅ |
| Firefox | 11+ | ✅ |
| Safari | 5+ | ✅ |
| Edge | 12+ | ✅ |
| IE | 10+ | ✅ |
| iOS Safari | 4.2+ | ✅ |
| Android | 4.4+ | ✅ |
SSE
| Browser | Version | Support |
|---|---|---|
| Chrome | 6+ | ✅ |
| Firefox | 6+ | ✅ |
| Safari | 5+ | ✅ |
| Edge | 79+ | ✅ |
| IE | - | ❌ |
| iOS Safari | 5+ | ✅ |
| Android | 4.4+ | ✅ |
WebTransport
| Browser | Version | Support |
|---|---|---|
| Chrome | 97+ | ✅ |
| Firefox | 114+ | ✅ |
| Safari | - | ❌ |
| Edge | 97+ | ✅ |
| IE | - | ❌ |
| iOS Safari | - | ❌ |
| Android Chrome | 97+ | ✅ |
Infrastructure Considerations
| Protocol | Pros | Cons |
|---|---|---|
| WebTransport | Best performance, multiple streams, unreliable delivery | Requires UDP, limited proxy/CDN support |
| NATS WS | Direct broker, wildcards, low latency | Requires NATS port exposure, ES modules |
| WebSocket | Works with most LBs, nginx/HAProxy/Cloudflare support | May need sticky sessions, proxy timeout issues |
| SSE | Standard HTTP everywhere, no special config, CDN-friendly | 6 conn/domain (HTTP/1.1), unidirectional |
| REST API | Universal, cacheable, stateless | Polling overhead, higher latency |
Code Examples
Unified Client
class StreamClient {
constructor(game, options = {}) {
this.game = game;
this.protocol = options.protocol || 'auto';
this.onMessage = options.onMessage || console.log;
this.connection = null;
}
async connect() {
switch (this.protocol) {
case 'websocket':
return this.connectWebSocket();
case 'sse':
return this.connectSSE();
case 'webtransport':
return this.connectWebTransport();
case 'auto':
default:
return this.connectAuto();
}
}
async connectAuto() {
// Try WebTransport first (best performance)
if (typeof WebTransport !== 'undefined') {
try {
await this.connectWebTransport();
return;
} catch (e) {
console.log('WebTransport unavailable, trying WebSocket');
}
}
// Fallback to WebSocket
try {
await this.connectWebSocket();
} catch (e) {
console.log('WebSocket unavailable, trying SSE');
await this.connectSSE();
}
}
connectWebSocket() {
return new Promise((resolve, reject) => {
const proto = location.protocol === 'https:' ? 'wss:' : 'ws:';
const ws = new WebSocket(`${proto}//${location.host}/ws/${this.game}`);
ws.onopen = () => {
this.connection = ws;
resolve();
};
ws.onmessage = (e) => {
this.onMessage(JSON.parse(e.data));
};
ws.onerror = reject;
});
}
connectSSE() {
return new Promise((resolve) => {
const sse = new EventSource(`/sse/${this.game}`);
this.connection = sse;
sse.onopen = resolve;
sse.addEventListener('message', (e) => {
this.onMessage(JSON.parse(e.data));
});
sse.addEventListener('initial', (e) => {
this.onMessage(JSON.parse(e.data));
});
});
}
async connectWebTransport() {
const wt = new WebTransport('https://datastream.andrebassi.com.br:4433/wt');
await wt.ready;
this.connection = wt;
const reader = wt.datagrams.readable.getReader();
this.readDatagrams(reader);
}
async readDatagrams(reader) {
while (true) {
const { value, done } = await reader.read();
if (done) break;
const text = new TextDecoder().decode(value);
this.onMessage(JSON.parse(text));
}
}
disconnect() {
if (this.connection) {
if (this.connection.close) {
this.connection.close();
}
}
}
}
// Usage
const client = new StreamClient('crash', {
protocol: 'auto',
onMessage: (data) => {
console.log(`Round ${data.round_id}: ${data.extras}`);
}
});
await client.connect();
Recommendations
For Most Applications
Use SSE as the default choice:
- Simplest to implement
- Best proxy compatibility
- Auto-reconnection
- Sufficient for most real-time needs
For Interactive Applications
Use WebSocket when:
- Client needs to send data
- Bidirectional communication required
- Gaming, chat, collaboration
For Direct Broker Access
Use NATS WebSocket when:
- Need wildcard subscriptions (e.g., all games)
- Want lowest latency without backend hop
- Building internal dashboards
- Have NATS port accessible to clients
For Cutting-Edge Performance
Use WebTransport when:
- Ultra-low latency is critical
- You control the infrastructure
- Safari support isn't required
- Implementing fallbacks
For Universal Compatibility
Use REST API when:
- Need historical data
- Building mobile apps
- Caching is important
- Real-time not required
- Fallback for streaming protocols