Build PhyStack
Overview ยท Build PhyStack

Overview

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:

ApproachPathLatencyBest For
Twin MessagingVia hub50-200msCommands, status updates, most applications
Real-Time ChannelsDirect P2PUnder 10msVideo 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 are routed intelligently โ€” locally within a device when possible, or through the cloud hub when apps are on different devices. This is the recommended approach for most applications.

import { connectPhyClient } from '@phystack/hub-client';

const client = await connectPhyClient();
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)
instance.to(targetTwinId).emit('calibrateSensor', { target: 25 }, (result) => {
  console.log('Result:', result.status);
});

// Receive messages
instance.on('command', (data, respond) => {
  console.log('Received:', data);
  if (respond) respond({ status: 'success', message: 'Command processed' });
});

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.

// Using client and instance from the setup above
const instance = await client.getInstance();

// Create a DataChannel for low-latency messaging
const channel = await instance.getDataChannel(targetTwinId);
channel.send({ sensorReading: 42.5 });
channel.onMessage((data) => console.log('Received:', data));

// Listen for incoming MediaStream (e.g. camera feed)
instance.onMediaStream((stream) => {
  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

ScenarioRecommendedWhy
Send command to peripheralTwin MessagingNeed confirmation, works anywhere
Update screen displayTwin MessagingFire-and-forget, reliable delivery
Stream camera to screenReal-Time ChannelsVideo requires MediaStream
100+ sensor readings/secReal-Time ChannelsHigh frequency needs low latency
Control device behind VPNTwin MessagingWebRTC may not traverse VPN
Computer vision on videoReal-Time ChannelsDirect frame access needed
Sync configurationTwin MessagingWorks across any network
Large file transferReal-Time ChannelsDirect P2P avoids hub limits

Comparison Table

FeatureEventsActionsDataChannelsMediaStreams
PatternFire-and-forgetRequest-responseBidirectional streamAudio/video stream
PathVia hubVia hubDirect P2PDirect P2P
Latency50-200ms50-200ms + responseUnder 10msUnder 50ms
ConnectionStatelessStatelessRequires setupRequires setup
NetworkAnyAnyNeeds STUN/public IPNeeds STUN/public IP
Data sizeSmall JSONSmall JSONAny sizeVideo/audio
Best forNotificationsCommandsStreaming dataComputer vision, video

Next Steps

ยฉ 2026 ยท PhyStack. An Ombori company