DearScenario
← All Guides
DearScenario Player Guide

Control Video Playback from TouchDesigner over OSC

TouchDesigner thinks in channels — continuous streams of numbers flowing every frame. OSC is the protocol that matches that way of working. HTTP provides validation and queue-admission feedback. OSC fits when you want a live CHOP value to become the player’s volume or playback position in real time. This guide covers both, using DearScenario Player as the remote player.

OSC or HTTP? Pick by the shape of the control

DearScenario Player accepts both. The HTTP guide covers discrete commands with validation and queue-admission feedback. Reach for OSC when the control is continuous and real-time:

HTTPOSC
Best forDiscrete cues: load, play, pauseContinuous values: volume, seek
RateA few per secondTens of times per second, per frame
ConfirmationValidated and queued: {"ok": true}; playback observed separatelyNo success acknowledgement
TouchDesigner sourceWeb DAT / PythonOSC Out DAT, driven by callbacks or CHOP values

Use an OSC Out DAT for both cue changes and live values. The Player accepts single OSC messages and rejects bundles, so explicitly send unbundled messages.

Setup

  1. Install and launch DearScenario Player on the playback machine (see HTTP node setup guide)
  2. Put both machines on the same network
  3. Set network.enable_osc to true in the player's config.json, then restart the player. OSC uses UDP port 18290.

Discrete cues with an OSC Out DAT

Use an OSC Out DAT for one-shot commands — the things that happen at a moment, not continuously. Set its Network Address to the DearScenario Player IP and Port to 18290, then send messages with sendOSC():

n = op('oscout1')

# Load and play in one message (string + cue + loop)
n.sendOSC('/player/play_media', ['C:\\media\\scene_a.mp4', 0.0, 0], asBundle=False)

# Or prepare first; send play from a later callback after HTTP status shows ready
n.sendOSC('/player/prepare', ['C:\\media\\scene_a.mp4', 0.0, 0], asBundle=False)
# In the ready callback: n.sendOSC('/player/play', [], asBundle=False)

# Later
n.sendOSC('/player/pause', [], asBundle=False)

Drive these from any callback — a CHOP Execute DAT’s onValueChange when a sensor crosses a threshold, a Timer CHOP at a timeline point, or a button’s panel callback.

Continuous control from CHOP channels

Build volume or seek values as CHOP channels, then use a CHOP Execute DAT to send individual messages through the OSC Out DAT. This gives explicit control over the message format required by the Player.

  1. Scale volume to 0–1 or seek position to non-negative seconds.
  2. Name the channels volume and seek_sec.
  3. Point a CHOP Execute DAT at that CHOP and enable Value Change.
  4. Configure an OSC Out DAT named oscout1 in the same network with the Player IP and port 18290.
  5. Use this callback:
def onValueChange(channel, sampleIndex, val, prev):
    if channel.name == 'volume':
        address = '/player/volume'
        value = max(0.0, min(1.0, float(val)))
    elif channel.name == 'seek_sec':
        address = '/player/seek_sec'
        value = max(0.0, float(val))
    else:
        return

    op('oscout1').sendOSC(
        address, [value], asBundle=False,
        useNonStandardTypes=False, use64BitPrecision=False)
    return

Use the OSC Out DAT sendOSC() options to keep messages unbundled with 32-bit numeric arguments. An OSC Out CHOP path must meet the same packet rules; a bundle or a 64-bit value will be rejected. Limit the update rate to what the installation needs.

OSC address reference

DearScenario Player listens for OSC on port 18290. These are the addresses you will use most from TouchDesigner:

OSC AddressTypeAction
/player/play_medias, sfi, or siiLoad and play (atomic cue)
/player/prepares, sfi, or siiLoad and leave ready/paused
/player/play—Start or resume playback
/player/pause—Pause playback
/player/cue—Return to cue point while keeping media loaded
/player/stop—Stop and remove media
/player/seek_secfloat or intSeek to time in seconds
/player/volumefloat or intSet volume (float 0–1; int 0 or 1)
/player/muteintMute (1) or unmute (0)
/player/loopintEnable (1) or disable (0) looping

Real-time patterns

  • Audio-reactive volume: An Audio Analysis CHOP envelope, scaled to 0–1, sent to /player/volume through the value-change callback so playback level tracks the room or a live source
  • Scrub by interaction: A slider drives player/seek_sec
  • Sensor-gated cue + live value: An OSC Out DAT fires /player/play_media when a sensor trips, while a CHOP Execute DAT sends volume updates through the OSC Out DAT
  • Multi-node control: Send the same absolute value through OSC Out DATs aimed at different node IPs; this does not guarantee frame-synchronized playback

Reliability: OSC is fire-and-forget

OSC has no success acknowledgement. Later absolute-value updates can replace a dropped volume or seek message. HTTP {"ok": true} confirms validation and queue admission only; it does not prove a file loaded or playback started. If the show depends on that state, inspect state, source, and error through GET /api/status. Immediate OSC errors are sent to the sender’s UDP source port, not an arbitrary OSC In DAT port, and do not report later playback failures. A common split:

  • OSC for everything continuous and for low-stakes triggers
  • HTTP plus status observation for cues that require verified playback state, sent from a script (see the HTTP guide)

Where DearScenario Player fits

DearScenario Player is a playback API node: it runs headless on a Windows PC or Raspberry Pi, exposes the same command model over HTTP and OSC, and does not try to be the creative brain. TouchDesigner holds the logic and the live data; DearScenario Player turns it into video on a remote screen. OSC is simply the channel-native way to connect the two.


Next step: If your production also runs QLab, see how to trigger DearScenario Player from QLab over OSC, or put a low-cost Raspberry Pi node behind each screen.