Quick answer: MultiplayerSynchronizer broadcasts to every peer by default (public_visibility = true). For area-of-interest or team-only state, set public_visibility = false and call set_visibility_for(peer_id, visible) to scope updates.

You build a multiplayer game with 32 players per match. Every player’s transform is a MultiplayerSynchronizer. Bandwidth usage is terrible, and cheaters on the other side of the map know exactly where your stealth character is hiding. The fix is a visibility filter that only sends state to players who can actually see the object.

Visibility Modes

Each synchronizer has a boolean public_visibility:

AOI Example

extends Node

@onready var sync = $MultiplayerSynchronizer
@onready var aoi_area = $Area3D

func _ready():
    sync.public_visibility = false
    aoi_area.body_entered.connect(_on_peer_entered)
    aoi_area.body_exited.connect(_on_peer_exited)
    multiplayer.peer_disconnected.connect(_on_peer_disconnected)

func _on_peer_entered(body):
    if body.is_in_group("player"):
        sync.set_visibility_for(body.get_multiplayer_authority(), true)

func _on_peer_exited(body):
    if body.is_in_group("player"):
        sync.set_visibility_for(body.get_multiplayer_authority(), false)

func _on_peer_disconnected(peer_id):
    sync.set_visibility_for(peer_id, false)

The AOI Area3D is a large sphere around the object. When a player’s body enters, they become visible for this synchronizer. When they leave, they stop receiving updates. When they disconnect, cleanup removes them from the visibility table.

MultiplayerSpawner Visibility

Spawners also have a spawn_function and visibility_changed signal. Use visibility_changed to hide the object’s visual representation on peers who can’t see it, not just stop replicating state. A client with an invisible opponent still sees a stale ghost unless you handle the signal.

Performance

Per-peer visibility has a cost: every tick checks the per-peer table. For 100 synchronizers and 32 peers, that’s 3200 checks per tick. This is still cheaper than sending 3200 pointless packets, but if you have extreme object counts, use spatial partitioning (like grid cells) instead of per-object AOI.

Verifying

Enable multiplayer.debug logging and watch per-peer bandwidth. Before the fix, every client’s download matches every other client. After, bandwidth scales with nearby activity. Stealth-focused game? Test that invisible players don’t appear in a network packet sniffer.

“MultiplayerSynchronizer defaults to the simplest behavior: send to everyone. For anything larger than a 4-player lobby, you need a visibility filter.”

Related Issues

For spawner replication, see Godot MultiplayerSpawner not syncing. For RPC routing, see Godot multiplayer RPC not reaching peers.

Never ship a multiplayer game without per-peer visibility on non-public data. Bandwidth and cheat risk both explode otherwise.