Example

Live Streaming

Player movement replication at 20 Hz using Stream's binary channels. Clients send their own CFrame and velocity. The server relays each update to everyone else via channel:SendExcept. Clients interpolate smoothly between received frames.

How it works

1
Client sends { userId, cf, vel, anim, health } at 20 Hz via channel:Send()
2
Server receives via channel:On(function(data, player)), overwrites data.userId with the real sender
3
Server calls channel:SendExcept(player, data) — one relay pass, no extra module needed
4
Other clients receive via channel:On(function(data, _)), push frame to interpolation buffer
5
Client interpolates between buffered frames on RunService.Heartbeat

Shared schema

Define channels in a shared module required by both the server and the client. Both sides must see the same Stream.Channel() calls before Stream.Init() is called.

-- ReplicatedStorage/StreamChannels.luau
local RoExpress = require(game.ReplicatedStorage.RoExpress)
local Stream    = RoExpress("Stream")

local stateSchema = Stream.Schema.New({
    { "userId", "f64"         },  -- 8 B: set server-side
    { "cf",     "CFrame"      },  -- 28 B: full rotation
    { "vel",    "Vector3"     },  -- 12 B: linear velocity
    { "anim",   "u8"          },  -- 1 B:  animation state ID
    { "health", "u8"          },  -- 1 B:  0–255
})

local playerState = Stream.Channel("playerState", stateSchema, { maxRate = 30 })

Stream.Init()

return { playerState = playerState }

Server: MovementServer.luau

local channels = require(game.ReplicatedStorage.StreamChannels)
local ch        = channels.playerState

ch:On(function(data, player)
    -- Override userId so clients can't claim to be someone else
    data.userId = player.UserId
    -- Relay to every other client in one call
    ch:SendExcept(player, data)
end)

Client: MovementClient.luau

local RunService  = game:GetService("RunService")
local Players     = game:GetService("Players")
local LocalPlayer = Players.LocalPlayer
local channels   = require(game.ReplicatedStorage.StreamChannels)
local ch          = channels.playerState
local buffers     = {}   -- { [userId] = { cf, vel, t } }

-- Send own state at 20 Hz
local accum = 0
RunService.Heartbeat:Connect(function(dt)
    accum += dt
    if accum < 0.05 then return end
    accum = 0
    local char = LocalPlayer.Character
    if not char then return end
    local hrp = char:FindFirstChild("HumanoidRootPart")
    if not hrp then return end
    ch:Send({
        userId = LocalPlayer.UserId,   -- server overwrites this
        cf     = hrp.CFrame,
        vel    = hrp.AssemblyLinearVelocity,
        anim   = getCurrentAnim(),
        health = math.floor(char.Humanoid.Health),
    })
end)

-- Receive other players' state
-- Note: second arg is nil on the client — use _ to signal it's intentionally ignored
ch:On(function(data, _)
    buffers[data.userId] = { cf = data.cf, vel = data.vel, t = os.clock() }
end)

-- Interpolate on every frame
RunService.Heartbeat:Connect(function(dt)
    for userId, buf in buffers do
        local player = Players:GetPlayerByUserId(userId)
        if player and player.Character then
            smoothMove(player.Character, buf.cf, buf.vel, dt)
        end
    end
end)

See also

Stream | schema, channels, delta compression  ·  FPS Guide | lag compensation with history buffer  ·  Stream: Server Side | all send methods, rate limiting  ·  Stream: Client Side | throttling, unsubscribing