Photon Realtime Interest Groups to Reduce Bandwidth in Multiplayer Games
Learn how Photon Realtime Interest Groups filter network traffic by subscribing clients to byte-coded channels, cutting bandwidth in scalable multiplayer games, with a C# example.
24 May 2026, 03:17 UTC

Problem: Unnecessary Network Traffic in Dense Multiplayer Scenes
When many players occupy the same virtual space, each client receives every position update, chat message, or object event, even if those events affect only faraway avatars. This wastes bandwidth and can cause latency spikes, especially on mobile or congested networks.
Thesis: Interest Groups Limit Each Client's Stream to What Matters
Photon Realtime Interest Groups are byte-valued channels (0-255). Group 0 is the default global group; any client subscribed to a group receives events sent to that group. By adding or removing players from custom groups based on position, team, or game state, you can implement area-of-interest (AOI) filtering without writing custom routing logic. The feature behaves the same across Photon Realtime SDKs (C#, C++, Java, Objective-C) and fits into PUN and Fusion workflows.
Worked Example: Position-Based Group Subscription (C#)
Assume a simple 2D arena divided into a 10x10 grid. Each grid cell maps to a custom Interest Group ID calculated as cellX * 10 + cellY + 1 (adding 1 to reserve group 0 for global chat). The client updates its group only when it crosses a cell boundary.
Client side
using Photon.Realtime;
using ExitGames.Client.Photon;
public class InterestGroupController : MonoBehaviour, IConnectionCallbacks, IMatchmakingCallbacks
{
private LoadBalancingClient lbClient;
private byte lastCustomGroup = 0;
void Start()
{
lbClient = new LoadBalancingClient();
lbClient.AddCallbackTarget(this);
lbClient.ConnectUsingSettings();
}
public void OnConnected()
{
lbClient.OpJoinRandomRoom();
}
public void OnJoinedRoom()
{
// Subscribe to the default group (0) for global chat
lbClient.OpChangeGroups(new byte[0], new byte[] { 0 });
}
// Call when the player's position changes, not every render frame
public void UpdateGroup(Vector2 position)
{
int cellX = Mathf.FloorToInt(position.x / 10f);
int cellY = Mathf.FloorToInt(position.y / 10f);
byte targetGroup = (byte)((cellX * 10) + cellY + 1); // 1-100
if (targetGroup == lastCustomGroup) return; // throttle: no change, no call
byte[] groupsToRemove = lastCustomGroup == 0 ? new byte[0] : new byte[] { lastCustomGroup };
lbClient.OpChangeGroups(groupsToRemove, new byte[] { targetGroup });
lastCustomGroup = targetGroup;
}
}
Run this in your Unity project with the Photon Realtime Unity SDK referenced. The client needs an active room connection before OpChangeGroups has any effect.
Sending events to a specific group
lbClient.OpRaiseEvent(
eventCode: 1,
customEventContent: positionData,
raiseEventOptions: new RaiseEventOptions { InterestGroup = targetGroup },
sendOptions: new SendOptions { Reliability = true });
Only clients currently subscribed to targetGroup receive the event. Use unreliable delivery for high-frequency position updates and reliable delivery for infrequent actions such as chat or ability triggers.
Trade-off and Limitation
Changing a player's Interest Group too frequently increases server CPU load because the server must update subscription lists. Batch updates or throttle them - for example, only call OpChangeGroups when the player crosses a cell boundary, as in the example above. Interest Groups also do not replace a full interest-management design: you must still choose grouping boundaries that avoid stale data (players receiving outdated positions) and cheating opportunities (a client staying in a low-traffic group while gaining unfair information). Group changes initiated by the client also require trust in the client unless you validate them server-side with a plugin.
Actionable Closing
- Keep group 0 for essential global messages such as chat or match start/end signals.
- Define a spatial grid or team-based mapping that fits your game's latency tolerance.
- Call
OpChangeGroupsonly when the calculated group actually changes. - Verify with two clients in different groups: send an event to each group and confirm each client receives only its own group's events.
- Measure bandwidth with a network profiler (e.g., Wireshark) with and without Interest Groups; traffic should drop roughly in proportion to the events filtered out.
- Monitor server CPU during load tests; if you see spikes, increase the update interval or merge adjacent cells into larger groups.
Treated as a lightweight built-in filtering layer, Interest Groups reduce unnecessary bandwidth while keeping the standard Photon Realtime workflow intact.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.