Taming Fan-Out in Photon Engine with Room Properties and Interest Groups
Use Room Properties for declarative matchmaking and Interest Groups for in-game culling to control fan-out in Photon Engine Rooms, with explicit reliability choices per message type.
21 Apr 2026, 03:01 UTC

Thirty-two players in one Room and every client receiving every position update is the classic fan-out problem. The useful takeaway is to keep matchmaking declarative with Room Properties and Player Custom Properties, then use Interest Groups for in-game culling and choose delivery semantics per message type.
The concrete problem
Photon Engine organizes multiplayer sessions as Rooms with a configurable max players. A Room can broadcast events to all occupants. Without culling, a 32-player arena with 20 Hz position updates means each client processes 31 updates per tick even when most players are off screen.
Interest Groups provide built-in interest management. Each client subscribes to numeric groups and only receives events published to subscribed groups. That lets you replace broadcast with selective delivery for spatial or logical culling.
Declarative matchmaking with Room Properties
Room Properties and Player Custom Properties are replicated to clients and can be used as filters for join requests. This keeps lobby logic declarative instead of polling.
Typical setup on the host or first joiner, run in game client code with permission to modify Room state:
// set once when Room is created
SetRoomProperties({
mode: "arena",
map: "grid01",
maxPlayers: 32
}, true)A joining client can request a Room with matching properties:
// join with filter, client side
JoinRoom(filter: {
mode: "arena",
map: "grid01"
})Expected check: the join succeeds only when a Room with those properties exists and has capacity. Risk: frequent large updates to Room Properties increase bandwidth and server load. Keep properties small and change them infrequently.
In-game culling with Interest Groups
Once inside the Room, move from matchmaking to bandwidth control. Interest Groups let you publish an event to a numeric group and only subscribed clients receive it.
Worked pattern for a grid arena. Divide the map into cells and assign each cell a group id. A player subscribes to its own cell plus neighbors.
// client side after spawn, no special permissions needed
Subscribe(ownGroup)
Subscribe(neighborGroups)
// publish position update only to relevant group
PublishEvent(
code: 1,
data: {x, y, tick},
reliability: "unreliable",
targetGroup: ownGroup
)Delivery semantics matter. The SDK exposes separate semantics for events, commonly reliable ordered versus unreliable unordered. Map gameplay data to the trade-off:
- Position updates: unreliable unordered for lowest latency.
- Hit confirmations or state changes: reliable ordered.
Limitation: Interest Groups limits, group ID ranges, and subscription behavior are implementation defined and may vary by platform and SDK family. Custom properties have size and update frequency limits. Do not use properties for per-tick state.
How to verify without assuming
Open the SDK sample project for Rooms and inspect how Room Properties are set and filtered during join attempts within the shipped examples. That confirms the property names and filter shape you can use in your SDK family.
For Interest Groups, create a test Room with two clients. Subscribe client A to group 5 and client B to group 6. Publish an event targeted to group 5. Observe delivery scope to confirm selective receipt. Review the SDK API surface for event publishing methods and check parameters for reliability mode and interest group targeting to confirm available options.
Keep the separation: Room Properties for lobby filtering, Interest Groups for in-game culling, and explicit reliability choices per message type. That combination controls fan-out while keeping matchmaking declarative.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.