Skip to content

Cloud gaming

Last updated View as MarkdownAgent setup

Run Freedoom in a Cloudflare Container, stream its audio and video through Realtime SFU, and send browser input back over DataChannels. Multiple viewers can watch while one browser tab controls the game.

This experimental example demonstrates a native publisher and browser receivers. The same media-and-control topology can inform remote application and device interfaces.

Open the example

How it works

The Container captures H.264 video and Opus audio. Its native publisher sends both tracks through a WebRTC connection to Realtime SFU. Each browser uses one SFU session and PeerConnection for received media and input DataChannels.

Application boundary
Browser tab
Worker
Game state
Realtime SFU
Container publisher
Access identity + per-tab capability · One SFU session and PeerConnection per browser tab
App request
Typed RPC
Start or stop
Register publisher
Create SFU resources
Publish H.264 + Opus
Publish two input channels
Subscribe to media + channels
Enable controller replies
Reliable keyboard + buttons
Replaceable pointer movement
Forward controller input

A Worker authenticates browser requests and holds the SFU credentials. A Durable Object owns the game run, viewer membership, controller selection, and resource cleanup. The Container's publisher and the browser exchange media and input through the SFU.

Requirements

Run and verify

  1. Prepare the example. Run:

    git clone https://github.com/cloudflare/realtime-examples.git
    cd realtime-examples/cloud-gaming
    npm install

    Create the ignored configuration file:

    cp .dev.vars.example .dev.vars
    chmod 600 .dev.vars

    Set REALTIME_SFU_APP_ID and REALTIME_SFU_BEARER_TOKEN to your SFU app values.

  2. Start the application. With your container engine running, start development:

    npm run dev

    Open http://localhost:8787/. The local identity path works on loopback hosts only.

  3. Start the game. Select Start and wait for Running. Open a second browser tab and confirm both tabs receive video and audio.

  4. Take control. In one tab, select Take control. Once control is ready, select the game to capture the pointer, then use the keyboard and mouse. The other tab should remain view-only.

    Use Send Esc for the game menu. The browser's Escape action releases pointer lock. Select Release control to allow another tab to take control.

For deployment, follow the example's deployment instructions. They install the SFU values as Worker secrets and configure the Access team domain and audience. Protect the hostname with the matching Access policy before inviting viewers.

Choose delivery settings for input

The example uses separate named channels for discrete actions and replaceable input:

Input Delivery Reason
Keyboard, buttons, wheel, and reset Reliable and ordered Preserve discrete actions and their order
Pointer movement Unordered, maxRetransmits: 0 New movement replaces older movement

The publisher chooses each channel's policy, and every subscriber mirrors it. Each browser uses the matching delivery settings with its own allocated channel ID. Refer to DataChannel delivery settings.

Control ownership

Viewers initially subscribe with canReply: false and acknowledge channel readiness with waitForAck. The backend grants reply access by updating the selected viewer's existing subscriptions.

The application identifies each controller selection with a generation. The browser sends input only after the publisher confirms its viewer and generation. The application releases held input when control changes, pointer lock is lost, or the run ends.

canReply restricts the SFU return path. The backend still authenticates and authorizes the operator. Refer to reply access and the example's architecture.

Stop and clean up

Select Stop game and wait for Offline before deleting a deployment. The application stops the Container and closes known SFU tracks and DataChannels. Follow the cleanup instructions to remove the Worker and Access application.

A failed media pipeline ends the run; transparent publisher restart is not implemented.

Adapt the example

The example implements one fixed game slot and one controller. Admission limits, rate limiting, and Access policy provisioning are deployment responsibilities.

For a shared CAD viewer or simulation, replace Freedoom and adapt application startup, capture, and input handling. Preserve viewer authorization, controller ownership, and cleanup. Add the file ownership and save/export behavior your application needs. Refer to production integration for the application policies and controls to add.

Inspect the implementation

Inspect the example's input channel definitions to compare reliable controls with transient pointer movement.

Troubleshooting

Use the troubleshooting guide to diagnose Container startup, media, and control failures.

Try another example

Pocket Radio applies a related observer/controller pattern on embedded hardware.

Was this helpful?