This document is about: FUSION 2
SWITCH TO

Photon Fusion 2 Network Properties Explained

This page accompanies the Photon Fusion 2 Network Properties Explained — How to Sync Player Data in Unity video and explains how to synchronize persistent player data in Photon Fusion Shared Mode.

Use this page when implementing synchronized values such as player colors, health, names, scores, equipment, character selections, animation state, or other data that every connected client must observe consistently.

If you want to dive deeper into Network Properties, take a look here.

Photon Fusion 2 Network Properties Explained — How to Sync Player Data in Unity (Full Tutorial)

Overview

Networked properties are Fusion state values that are replicated between peers.

When the State Authority changes a Networked property, Fusion includes the new value in the object’s synchronized state. Other clients then receive that value and can update their local presentation.

This tutorial builds a small Shared Mode example in which:

  • players spawn at different positions;
  • each player receives an initial color;
  • the selected color is stored as a Networked property;
  • players can change their own color during gameplay;
  • all connected clients observe the change;
  • [OnChangedRender] updates the local Unity visuals.

The tutorial also demonstrates an important optimization: synchronizing a one-byte color index instead of the complete color value.

Video Timeline

Time Section
00:00 Introduction
00:22 Setting up the project
01:18 Getting the Fusion SDK
03:02 Creating the player prefab
04:43 Implementing the player spawner
07:44 Spawning players in Shared Mode
08:15 Synchronizing player positions
08:45 Creating NetworkedPlayerColor
10:29 Using the [Networked] attribute
13:30 Applying synchronized colors
14:16 Building and testing multiplayer
15:37 Changing colors dynamically
16:23 Using [OnChangedRender]
17:00 Optimization and summary

What Are Networked Properties?

A Networked property is a value stored as part of a Fusion NetworkObject’s replicated state.

Examples include:

  • player health;
  • player color;
  • display name;
  • score;
  • ammunition;
  • selected weapon;
  • character skin;
  • ready state;
  • current team;
  • game phase.

A Networked property is declared inside a NetworkBehaviour using the [Networked] attribute.

Example:

C#

[Networked]
public float Health { get; set; }

When the correct authority changes Health, Fusion synchronizes the new value to the other clients.

Networked State Compared with Local State

A normal field exists only inside the local Unity process:

C#

private float _health;

Changing it does not automatically notify other Fusion clients.

A Networked property is part of the object’s replicated state:

C#

[Networked]
public float Health { get; set; }

Use a Networked property when the current value must be known by other peers or late joiners.

Use a normal local field for values that exist only on one client, such as:

  • cached component references;
  • local UI objects;
  • temporary visual effects;
  • local input buffers;
  • camera references;
  • non-networked animation helpers.

Networked Properties Represent State

Networked properties are intended for persistent current state.

For example:

Player color = Blue

A client joining later can receive that current value.

This differs from an RPC, which represents a message or event that happened at a specific moment.

Use Networked properties for values that answer:

What is the object’s current state?

Use RPCs for actions that answer:

What event just happened?

Authority and Networked Properties

Only the peer with the appropriate State Authority should modify a Networked property.

A client can technically assign a value locally on an object it does not control, but that change does not become the authoritative synchronized state.

It may appear temporarily as a local prediction and later be replaced by the actual State Authority value.

Before changing a Networked property, check:

C#

Object.HasStateAuthority

Example:

C#

if (Object.HasStateAuthority)
{
    Health = 100f;
}

State Authority in Shared Mode

In Shared Mode, the client that spawns an object generally receives State Authority over it unless authority is configured or transferred differently.

For player objects, each client commonly spawns and controls its own player.

This means:

  • Player 1 writes Player 1’s Networked properties;
  • Player 2 writes Player 2’s Networked properties;
  • proxies display the synchronized results.

The exact authority model depends on the project’s spawning and object configuration.

State Authority Compared with Input Authority

State Authority controls who may write the object’s Networked state.

Input Authority identifies the player associated with the object’s input.

Authority Type Purpose
State Authority Writes authoritative Networked properties.
Input Authority Identifies the player whose input is associated with the object.

In the tutorial:

  • HasStateAuthority determines whether the client may change ColorIndex;
  • Object.InputAuthority.PlayerId is used to derive the initial color.

Do not treat Input Authority alone as permission to write Networked state.

Supported Networked Types

Fusion supports specific network-compatible data types.

Common examples include:

  • byte;
  • int;
  • float;
  • bool or NetworkBool;
  • enums;
  • Vector2;
  • Vector3;
  • Quaternion;
  • Color;
  • Color32;
  • PlayerRef;
  • NetworkString;
  • Fusion Networked collections;
  • structs implementing INetworkStruct.

Complex Unity objects cannot be synchronized directly.

Do not attempt to synchronize:

  • GameObject;
  • Transform;
  • SpriteRenderer;
  • Image;
  • Material;
  • arbitrary MonoBehaviours;
  • scene component references.

Synchronize the data required to reconstruct their state locally.

Synchronize Data, Not Unity Components

Suppose a player UI contains a Unity Image.

Do not synchronize the Image component.

Instead, synchronize a simple value such as:

C#

[Networked]
public byte ColorIndex { get; set; }

Each client uses the index to update its own local Image, SpriteRenderer, or material.

This keeps Networked state compact and separates networking from presentation.

Project Setup

Begin with a new Unity scene and install the current Fusion SDK.

The basic setup process is:

  1. Create or open a Unity project.
  2. Import Photon Fusion.
  3. Create a Fusion application in the Photon Dashboard.
  4. Copy its App ID.
  5. Add the App ID to the Fusion project settings.
  6. Verify that the project compiles.

Adding Basic Fusion Networking

Fusion provides a setup tool for creating a simple connection flow.

Open:

Tools > Fusion > Scene > Setup Networking in Scene

This creates objects such as:

  • Prototype Runner;
  • Prototype Network Start.

When the scene starts, the sample connection UI allows you to run the project in different network modes.

For this tutorial, use Shared Mode.

Creating the Player Prefab

Create the player hierarchy:

Player
└── Graphics
    └── Body

The root object contains the network and gameplay components.

The child objects contain the visual representation.

The example uses simple 2D shapes, but the same architecture applies to:

  • 3D characters;
  • sprites;
  • vehicles;
  • avatars;
  • custom models.

Making the Player Networked

Add the following components to the player root:

  • NetworkObject;
  • NetworkTransform;
  • NetworkedPlayerColor.

NetworkObject gives the prefab a Fusion network identity.

NetworkTransform synchronizes its position and rotation.

After configuring the object:

  1. Create a Prefabs folder.
  2. Drag the player into the folder.
  3. Delete the temporary scene instance.
  4. Ensure Fusion recognizes the prefab as a network prefab.

The player will be spawned at runtime.

NetworkTransform

NetworkTransform synchronizes transform state without requiring a custom Networked position property.

For this example, it handles:

  • player position;
  • player rotation where applicable;
  • proxy interpolation.

The player color is synchronized separately through NetworkedPlayerColor.

Creating the Player Spawner

Create:

PlayerSpawner

The spawner inherits from:

C#

SimulationBehaviour

and implements:

C#

IPlayerJoined

IPlayerJoined provides a callback when a player enters the Fusion session.

PlayerSpawner Fields

The spawner needs:

  • a player prefab;
  • an array of spawn points.

Example:

C#

using Fusion;
using UnityEngine;

public class PlayerSpawner :
    SimulationBehaviour,
    IPlayerJoined
{
    [SerializeField]
    private NetworkObject _playerPrefab;

    [SerializeField]
    private Transform[] _spawnPoints;

    public void PlayerJoined(PlayerRef player)
    {
    }
}

Spawning Only the Local Player

Every peer can receive the player-joined callback.

The tutorial checks whether the joining player is the local player:

C#

if (player != Runner.LocalPlayer)
{
    return;
}

That peer then spawns its own player object.

This avoids every client spawning duplicate objects for the same player.

Selecting a Spawn Point

Use the player index to select a spawn point:

C#

int spawnIndex =
    player.AsIndex %
    _spawnPoints.Length;

The modulo operation ensures that the value stays within the array bounds.

For example, with three spawn points:

Player Index Spawn Point
0 0
1 1
2 2
3 0

Spawning the Player

Spawn through the Fusion runner:

C#

public void PlayerJoined(PlayerRef player)
{
    if (player != Runner.LocalPlayer)
    {
        return;
    }

    if (_spawnPoints == null ||
        _spawnPoints.Length == 0)
    {
        Debug.LogError(
            "No player spawn points configured."
        );

        return;
    }

    int spawnIndex =
        player.AsIndex %
        _spawnPoints.Length;

    Transform spawnPoint =
        _spawnPoints[spawnIndex];

    Runner.Spawn(
        _playerPrefab,
        spawnPoint.position,
        spawnPoint.rotation,
        player
    );
}

The final PlayerRef argument assigns the spawned object’s Input Authority.

Because the local Shared Mode client performs the spawn, it also normally receives State Authority over the new object.

Adding the Spawner to Prototype Runner

Attach PlayerSpawner to:

Prototype Runner

This is important because the runner hosts the simulation callbacks and player-join events.

Assign:

  • the player prefab;
  • all available spawn points.

When a player joins, the runner calls PlayerJoined.

Testing Basic Spawning

Before implementing color synchronization:

  1. Enter Play Mode.
  2. Start a Shared Mode client.
  3. Confirm that the local player spawns.
  4. Start a second client.
  5. Confirm that both players exist.
  6. Verify that NetworkTransform synchronizes movement or position.

Fix spawning or transform issues before adding custom Networked properties.

Creating NetworkedPlayerColor

Create:

NetworkedPlayerColor

The script inherits from:

C#

NetworkBehaviour

It contains:

  • an array of body-part renderers;
  • an array of predefined colors;
  • a Networked color index;
  • initialization logic;
  • local input;
  • a render-side change callback.

Why Synchronize a Color Index?

Fusion can synchronize Color and Color32.

However, the sample uses a predefined palette.

Instead of synchronizing the complete color value, synchronize only the selected index.

Approximate value sizes:

Data Approximate Size
Color Four floating-point channels
Color32 Four byte channels
byte index One byte

If the available colors already exist in the same order on every client, the index is sufficient.

Example:

0 = Red
1 = Blue
2 = Green
3 = Yellow

Synchronizing 2 tells every client to use Green.

Declaring the Networked Property

C#

[Networked]
public byte ColorIndex { get; set; }

Fusion Networked properties are commonly declared as auto-properties.

Do not implement a custom backing field unless the Fusion API explicitly supports the chosen pattern.

Adding OnChangedRender

Add a render-side change callback:

C#

[Networked,
 OnChangedRender(nameof(OnPlayerColorChanged))]
public byte ColorIndex { get; set; }

When the rendered value changes, Fusion calls:

C#

private void OnPlayerColorChanged()
{
    ApplyPlayerColor();
}

This keeps visual updates separate from the Networked value itself.

Complete NetworkedPlayerColor Example

C#

using Fusion;
using UnityEngine;

public class NetworkedPlayerColor :
    NetworkBehaviour
{
    [SerializeField]
    private SpriteRenderer[] _bodyParts;

    [SerializeField]
    private Color[] _playerColors;

    [Networked,
     OnChangedRender(nameof(OnPlayerColorChanged))]
    public byte ColorIndex { get; set; }

    public override void Spawned()
    {
        if (_playerColors == null ||
            _playerColors.Length == 0)
        {
            Debug.LogError(
                "No player colors configured.",
                this
            );

            return;
        }

        if (Object.HasStateAuthority)
        {
            int playerId =
                Object.InputAuthority.PlayerId;

            int initialIndex =
                playerId %
                _playerColors.Length;

            ColorIndex =
                (byte)initialIndex;
        }

        ApplyPlayerColor();
    }

    private void Update()
    {
        if (!Object.HasStateAuthority)
        {
            return;
        }

        if (!Input.GetKeyDown(KeyCode.W))
        {
            return;
        }

        if (_playerColors == null ||
            _playerColors.Length == 0)
        {
            return;
        }

        int nextIndex =
            (ColorIndex + 1) %
            _playerColors.Length;

        ColorIndex =
            (byte)nextIndex;
    }

    private void OnPlayerColorChanged()
    {
        ApplyPlayerColor();
    }

    private void ApplyPlayerColor()
    {
        if (_playerColors == null ||
            _playerColors.Length == 0)
        {
            return;
        }

        if (ColorIndex >=
            _playerColors.Length)
        {
            return;
        }

        Color currentColor =
            _playerColors[ColorIndex];

        foreach (
            SpriteRenderer bodyPart
            in _bodyParts)
        {
            if (bodyPart == null)
            {
                continue;
            }

            bodyPart.color =
                currentColor;
        }
    }
}

Initializing the Color in Spawned

Spawned() is Fusion’s network-object initialization callback.

It is called when the NetworkObject becomes available to the local peer.

For the authoritative player:

C#

if (Object.HasStateAuthority)
{
    int playerId =
        Object.InputAuthority.PlayerId;

    int colorIndex =
        playerId %
        _playerColors.Length;

    ColorIndex =
        (byte)colorIndex;
}

This gives each player a starting color derived from their player ID.

Why Apply the Color in Spawned?

The callback associated with [OnChangedRender] responds to rendered changes.

The object should also apply its current visual state when it initially spawns.

Calling:

C#

ApplyPlayerColor();

from Spawned() ensures the current synchronized value is reflected immediately.

This is particularly important for:

  • proxies;
  • late joiners;
  • objects whose Networked state was initialized before the local view existed.

Changing the Color

The sample uses the W key:

C#

private void Update()
{
    if (Object.HasStateAuthority &&
        Input.GetKeyDown(KeyCode.W))
    {
        int nextIndex =
            (ColorIndex + 1) %
            _playerColors.Length;

        ColorIndex =
            (byte)nextIndex;
    }
}

Only the player with State Authority can change the value.

When ColorIndex changes:

  1. Fusion records the new Networked state.
  2. Other clients receive it.
  3. OnPlayerColorChanged() executes during rendering.
  4. Each client updates its local SpriteRenderers.

Why Use Update for the Key Press?

The example uses Unity’s Input.GetKeyDown, which is a rendered-frame input API.

This is acceptable for a simple visual demonstration.

For gameplay-critical input, use Fusion’s normal input collection and consume the result during FixedUpdateNetwork().

A color-selection menu could also call a public method rather than reading a keyboard key directly.

Public Color Selection Method

A reusable implementation can expose:

C#

public void SelectColor(byte colorIndex)
{
    if (!Object.HasStateAuthority)
    {
        return;
    }

    if (colorIndex >=
        _playerColors.Length)
    {
        return;
    }

    ColorIndex = colorIndex;
}

A local UI button can then request a specific color.

For competitive or restricted customization, validate the selection on the appropriate authority.

Applying the Color Locally

The visual method reads the synchronized index:

C#

private void ApplyPlayerColor()
{
    if (ColorIndex >=
        _playerColors.Length)
    {
        return;
    }

    Color currentColor =
        _playerColors[ColorIndex];

    foreach (
        SpriteRenderer bodyPart
        in _bodyParts)
    {
        bodyPart.color =
            currentColor;
    }
}

The SpriteRenderers are not Networked.

Every client updates its own renderers using the same synchronized data.

Multiple Body Parts

The example uses an array so one color can be applied to several character parts.

Examples include:

  • body;
  • arms;
  • helmet;
  • weapon accent;
  • outline;
  • UI marker.

The array allows the same Networked value to drive several local visual components.

Shared Color Palette

Every client must use the same palette order.

For example, if index 1 is Blue on one client but Green on another, the Networked property remains synchronized but the visual result differs.

Keep the color palette consistent through:

  • the same prefab;
  • a shared ScriptableObject;
  • a versioned configuration asset;
  • build validation.

Color Index Validation

Always validate the index before reading the array:

C#

if (ColorIndex >= _playerColors.Length)
{
    return;
}

This prevents an out-of-range exception if:

  • the palette changes;
  • invalid state is received;
  • a prefab is misconfigured;
  • a value is set through debugging tools.

Networked Property Optimization

Synchronize the smallest value that fully represents the required state.

Examples:

Requirement Possible Networked Value
Predefined player color byte palette index
Selected skin byte or ushort skin index
Team small enum
Ready state NetworkBool
Weapon selection compact weapon ID
Current animation state byte enum or compact parameter
Player name fixed-capacity NetworkString
Health quantized integer where precision permits

Do not synchronize a large structure when a compact identifier produces the same result.

Derived State

Some values do not need to be synchronized because every client can derive them.

For example, if a player’s display outline is always brighter than their synchronized base color, synchronize only the base color index and derive the outline locally.

Avoid storing both:

BaseColor
OutlineColor

when one can be calculated from the other.

Avoid Writing Unchanged Values

Before assigning frequently changing Networked state, check whether the value actually differs.

Example:

C#

if (ColorIndex != requestedIndex)
{
    ColorIndex = requestedIndex;
}

Fusion handles state replication efficiently, but avoiding unnecessary writes makes the code’s intent clearer and can reduce avoidable change activity.

Networked Property Change Flow

The complete color flow is:

  1. The authoritative player presses W.
  2. ColorIndex changes.
  3. Fusion stores the new object state.
  4. Fusion sends the change to interested peers.
  5. Proxy render state updates.
  6. [OnChangedRender] invokes the callback.
  7. ApplyPlayerColor() changes local SpriteRenderers.

Late Joiners

Networked properties represent current state.

A player joining after another player has changed color receives the current ColorIndex.

The late joiner does not need the history of every color change.

They only need the latest value.

This is one of the main differences between Networked state and an RPC-only implementation.

Testing with Multiple Clients

Test the project with at least two instances.

Suggested process:

  1. Create a standalone build.
  2. Start one client from the build.
  3. Start another client in the Unity Editor.
  4. Connect both clients to the same Shared Mode session.
  5. Confirm that each player spawns at a different point.
  6. Confirm that each player has an initial color.
  7. Press W on Player 1.
  8. Verify that only Player 1 changes color.
  9. Confirm that Player 2 sees the new color.
  10. Press W on Player 2.
  11. Confirm that the change appears on both clients.

Testing Authority

Verify that a client cannot change a remote player’s synchronized color.

Each local client should modify only the object for which it has State Authority.

Add temporary logging if required:

C#

Debug.Log(
    $"Player {Object.InputAuthority} " +
    $"State Authority: " +
    $"{Object.HasStateAuthority}"
);

Testing Late Joiners

  1. Start Player 1.
  2. Change Player 1’s color.
  3. Start Player 2 afterwards.
  4. Join the same session.
  5. Verify that Player 2 sees Player 1’s current color.

This confirms that the value exists as synchronized state rather than only as a local event.

Player Color as an Example Pattern

The same architecture can be reused for other player data.

Health

C#

[Networked,
 OnChangedRender(nameof(OnHealthChanged))]
public float Health { get; set; }

The callback updates a local health bar.

Player Name

C#

[Networked]
public NetworkString<_32>
    PlayerName { get; set; }

The local UI displays the value above the player.

Selected Skin

C#

[Networked,
 OnChangedRender(nameof(OnSkinChanged))]
public byte SkinIndex { get; set; }

The callback activates the correct model or material.

Score

C#

[Networked]
public int Score { get; set; }

The scoreboard reads the current synchronized value.

Change Callbacks and Presentation

Use [OnChangedRender] for visual updates that should occur when a rendered Networked value changes.

Examples include:

  • changing a material;
  • updating a health bar;
  • changing player name text;
  • displaying team colors;
  • updating an equipment model;
  • switching an animation layer.

Do not use a render callback as the authoritative place for gameplay state changes.

The callback is for presentation based on state that has already changed.

Gameplay Logic and Render Logic

Logic Recommended Location
Change authoritative health FixedUpdateNetwork(), RPC validation, or authoritative gameplay system
Apply health-bar fill Render() or [OnChangedRender]
Change selected weapon ID Authoritative simulation logic
Enable local weapon model Render callback
Change player color index State Authority
Apply color to SpriteRenderers Render callback

Keep state mutation separate from visual application.

Networked Collections

For multiple synchronized values, Fusion provides Networked collections such as:

  • NetworkArray;
  • NetworkDictionary;
  • NetworkLinkedList.

Use them for bounded collections such as:

  • inventory slots;
  • lobby player data;
  • recent events;
  • team rosters.

Do not use a collection when one compact property is sufficient.

  1. Decide which value represents the authoritative state.
  2. Determine which peer should own State Authority.
  3. Choose the smallest supported Networked type.
  4. Declare the property in a NetworkBehaviour.
  5. Change it only from the correct authority.
  6. Use Spawned() to apply initial state.
  7. Use [OnChangedRender] or Render() for visual updates.
  8. Keep Unity component references local.
  9. Test proxies and late joiners.
  10. Inspect state size and change frequency.

Key Concepts

Concept Description
Networked property Value stored as part of Fusion’s replicated object state.
[Networked] Attribute declaring a Fusion-synchronized property.
NetworkBehaviour Base class that provides Fusion network-object functionality.
State Authority Peer allowed to write authoritative Networked state.
Input Authority Player associated with the object’s input.
NetworkObject Gives a GameObject a Fusion network identity.
NetworkTransform Synchronizes object position and rotation.
SimulationBehaviour Fusion behaviour used for runner-level simulation callbacks.
IPlayerJoined Interface receiving player-join callbacks.
PlayerRef Fusion identifier for a connected player.
Spawned() Initialization callback for a spawned NetworkObject.
[OnChangedRender] Invokes a render-side callback when a property’s rendered value changes.
Proxy Local representation of an object controlled by another peer.
Late joiner Client that enters after Networked state has already been established.

Best Practices

Use this checklist when working with Networked properties:

  • Synchronize state, not Unity component references.
  • Modify Networked properties only from the correct State Authority.
  • Use PlayerRef to identify players.
  • Use compact IDs or indexes for predefined data.
  • Apply synchronized state to local visuals.
  • Use Spawned() for initial visual setup.
  • Use [OnChangedRender] for presentation changes.
  • Keep gameplay mutations out of render callbacks.
  • Validate array indexes before using them.
  • Keep configuration arrays consistent on all clients.
  • Avoid synchronizing values that can be derived locally.
  • Avoid writing values that have not changed.
  • Test with multiple clients.
  • Test late joining.
  • Verify authority before accepting local input.
  • Use Networked collections only when several bounded values are required.

Common Pitfalls

Pitfall Why It Is a Problem
Changing a property without State Authority The value is not accepted as authoritative synchronized state.
Synchronizing a GameObject or SpriteRenderer Unity object references are not general Networked property data.
Synchronizing a complete color when an index is sufficient Uses more Networked state than necessary.
Forgetting to inherit from NetworkBehaviour The script cannot declare Fusion Networked properties correctly.
Using a normal field instead of [Networked] Other peers do not receive the value.
Not applying the value in Spawned() Initial or late-join visuals may not reflect current state immediately.
Updating gameplay state from [OnChangedRender] Render callbacks are presentation-side and not authoritative simulation logic.
Using different color-array orders on clients The same index produces different visual results.
Not checking palette length Modulo or array access can fail when no colors are configured.
Letting every client spawn every player Duplicate player objects can be created.
Assuming the final Runner.Spawn argument grants State Authority It assigns Input Authority; Shared Mode State Authority comes from the spawning and authority rules.
Reading local input on proxy objects A client may attempt to modify remote player representations.
Using an RPC alone for current color Late joiners would not automatically receive the current selection.

Summary

Networked properties synchronize persistent object state between Fusion clients.

In the tutorial, each player stores a Networked ColorIndex. The client with State Authority changes the index, Fusion replicates it, and [OnChangedRender] updates the local SpriteRenderers on every peer.

Synchronizing a compact palette index is more efficient than synchronizing the complete color value when every client already has the same predefined palette.

The same pattern applies to health, names, scores, skins, equipment, ready states, and other player data:

  1. Synchronize a compact authoritative value.
  2. Let the correct authority change it.
  3. Reconstruct the Unity presentation locally.

Last updated on

Back to top