This guide explains how twins communicate with each other in PhyStack. There are two fundamentally different approaches, each suited to different use cases.
Two Ways to Communicate
PhyStack provides two communication methods between twins:
| Approach | Path | Latency | Best For |
|---|---|---|---|
| Twin Messaging | Via hub | 50-200ms | Commands, status updates, most applications |
| Real-Time Channels | Direct P2P | Under 10ms | Video streaming, high-frequency sensors |
Start with Twin Messaging - it works across any network and covers most use cases. Only use Real-Time Channels when you specifically need streaming or sub-10ms latency.
Twin Messaging (Hub-Based)
Events and Actions route messages through the PhyStack hub. This is the recommended approach for most applications.
const instance = await client.getInstance();
// Broadcast event to subscribers (fire-and-forget)
instance.emit('statusUpdate', { status: 'ready' });
// Send event to specific twin (fire-and-forget)
instance.to(targetTwinId).emit('notification', { message: 'Hello' });
// Send action to specific twin (request-response)
const result = await instance.to(targetTwinId).emit('calibrateSensor', { target: 25 }, () => {});
console.log('Result:', result.status);
// Receive messages
instance.on('command', (data, respond) => {
console.log('Received:', data);
if (respond) respond({ status: 'success' });
});Why use it:
- Works across any network (VPNs, firewalls, cellular)
- No connection setup required
- Hub handles routing and delivery
- Simple API
Limitations:
- 50-200ms latency per message
- Best for small JSON payloads
- Not suitable for streaming
See Twin Messaging for the complete guide.
Real-Time Channels (WebRTC)
DataChannels and MediaStreams establish direct peer-to-peer connections for real-time communication.
// Create a DataChannel for low-latency messaging
const channel = await client.getDataChannel(targetTwinId);
channel.send({ sensorReading: 42.5 });
channel.onMessage((data) => console.log("Received:", data));
// Receive MediaStream video from a peer
client.onMediaStream(cameraTwinId, (stream) => {
// Get the underlying MediaStream for video element
videoElement.srcObject = stream.getStream();
});Why use it:
- Sub-10ms latency
- High throughput for large data
- Native video/audio streaming
- Direct device-to-device
Limitations:
- Requires compatible network (same LAN, STUN support, or public IP)
- May not work through strict firewalls or VPNs
- Connection setup overhead
- More complex error handling
See Real-Time Channels for the complete guide.
Decision Guide
| Scenario | Recommended | Why |
|---|---|---|
| Send command to peripheral | Twin Messaging | Need confirmation, works anywhere |
| Update screen display | Twin Messaging | Fire-and-forget, reliable delivery |
| Stream camera to screen | Real-Time Channels | Video requires MediaStream |
| 100+ sensor readings/sec | Real-Time Channels | High frequency needs low latency |
| Control device behind VPN | Twin Messaging | WebRTC may not traverse VPN |
| Computer vision on video | Real-Time Channels | Direct frame access needed |
| Sync configuration | Twin Messaging | Works across any network |
| Large file transfer | Real-Time Channels | Direct P2P avoids hub limits |
Comparison Table
| Feature | Events | Actions | DataChannels | MediaStreams |
|---|---|---|---|---|
| Pattern | Fire-and-forget | Request-response | Bidirectional stream | Audio/video stream |
| Path | Via hub | Via hub | Direct P2P | Direct P2P |
| Latency | 50-200ms | 50-200ms + response | Under 10ms | Under 50ms |
| Connection | Stateless | Stateless | Requires setup | Requires setup |
| Network | Any | Any | Needs STUN/public IP | Needs STUN/public IP |
| Data size | Small JSON | Small JSON | Any size | Video/audio |
| Best for | Notifications | Commands | Streaming data | Computer vision, video |
Next Steps
- Twin Messaging - Events, Actions, and the Instance API
- Real-Time Channels - WebRTC DataChannels and MediaStreams