Skip to main content

Integration API

The Daydream TouchDesigner plugin exposes a Python API for scripting, automation, and building custom interfaces.

Accessing the Extension

# Get the extension from your Daydream component
ext = op('/daydream').ext.Daydream

Public Contract

The extension exposes PUBLIC_CONTRACT with introspectable metadata:
PUBLIC_CONTRACT = {
    'extension_name': 'Daydream',
    'lifecycle_methods': ['Login', 'Start', 'Stop', 'ResetParameters'],
    'state_properties': ['state', 'Active', 'IsLoggedIn', 'ApiToken', 'stream_id', 'whip_url', 'whep_url'],
    'states': ['IDLE', 'CREATING', 'STREAMING', 'ERROR'],
    'listener_api': ['register_listener', 'unregister_listener'],
    'events': [
        'initialized',
        'login_started', 'login_success', 'login_failed',
        'stream_create_started', 'stream_created', 'stream_create_failed',
        'streaming_started', 'streaming_stopped',
        'params_update_sent', 'params_update_result',
        'state_changed', 'error',
    ],
}

GetCapabilities

Query runtime capabilities for the current model:
ext = op('/daydream').ext.Daydream
caps = ext.GetCapabilities()

# Returns:
# {
#     'backend': 'daydream',
#     'version': 'x.y.z',
#     'model': 'stabilityai/sdxl-turbo',
#     'supported_models': ['stabilityai/sdxl-turbo', 'stabilityai/sd-turbo', ...],
#     'controlnets': ['depth', 'canny', 'tile'],
#     'ip_adapter_types': ['regular', 'faceid'],
# }
Use this to build adaptive interfaces that show only available options.

State Properties

Read the current state:
ext = op('/daydream').ext.Daydream

# Current state
print(ext.state)  # 'IDLE', 'CREATING', 'STREAMING', or 'ERROR'

# Stream info
print(ext.stream_id)
print(ext.whip_url)
print(ext.whep_url)

# Auth status
print(ext.IsLoggedIn)
print(ext.Active)

Lifecycle Methods

Control the component programmatically:
ext = op('/daydream').ext.Daydream

# Start authentication flow
ext.Login()

# Start streaming (requires login)
ext.Start()

# Stop streaming
ext.Stop()

# Reset all parameters to defaults
ext.ResetParameters()

Event Listeners

Register a callback to receive lifecycle events without polling:
def on_daydream_event(event, payload):
    # payload always includes: owner_path, state, stream_id
    print(f"Event: {event}")
    print(f"Payload: {payload}")

ext = op('/daydream').ext.Daydream
ext.register_listener(on_daydream_event)

# Later, to stop listening:
# ext.unregister_listener(on_daydream_event)

Event Reference

EventPayloadDescription
initializedlogged_inExtension ready
login_startedauth_portAuth flow began
login_successSuccessfully logged in
login_failederrorLogin error
stream_create_startedmodelCreating stream
stream_createdwhip_url, model_idStream ready
stream_create_failederrorCreation error
streaming_startedwhip_url, whep_url, model_idStreaming active
streaming_stoppedprev_stream_idStreaming ended
params_update_sentchanged, paramsParameters sent
params_update_resultsuccess, errorUpdate response
state_changedfrom, to, reason, errorState transition
errorerror, context, will_retryError occurred

Example: State Machine Monitor

def on_event(event, payload):
    if event == 'state_changed':
        from_state = payload.get('from', 'unknown')
        to_state = payload.get('to', 'unknown')
        reason = payload.get('reason', '')
        print(f"State: {from_state}{to_state} ({reason})")

        # Update UI based on state
        if to_state == 'STREAMING':
            op('status_text').par.text = 'Live!'
            op('status_text').par.fontcolorr = 0
            op('status_text').par.fontcolorg = 1
            op('status_text').par.fontcolorb = 0
        elif to_state == 'ERROR':
            op('status_text').par.text = f'Error: {payload.get("error", "unknown")}'
            op('status_text').par.fontcolorr = 1
            op('status_text').par.fontcolorg = 0
            op('status_text').par.fontcolorb = 0

    elif event == 'streaming_started':
        whep_url = payload.get('whep_url')
        print(f"Watch at: {whep_url}")

    elif event == 'error':
        context = payload.get('context', 'unknown')
        error = payload.get('error', 'unknown error')
        will_retry = payload.get('will_retry', False)
        print(f"Error in {context}: {error}")
        if will_retry:
            print("Will retry automatically...")

ext = op('/daydream').ext.Daydream
ext.register_listener(on_event)

Example: Dynamic Parameter Control

Control parameters from CHOPs for audio-reactive visuals:
# In an Execute DAT triggered by a CHOP

def onValueChange(channel, sampleIndex, val, prev):
    daydream = op('/daydream')

    if channel.name == 'audio_bass':
        # Map bass to guidance scale
        guidance = tdu.remap(val, 0, 1, 1.0, 2.5)
        daydream.par.Guidance = guidance

    elif channel.name == 'audio_mid':
        # Map mids to delta
        delta = tdu.remap(val, 0, 1, 0.3, 0.8)
        daydream.par.Delta = delta

    elif channel.name == 'beat':
        # On beat, randomize seeds
        if val > 0.5:
            daydream.ext.Daydream.RandomizeSeeds()

Example: Custom Control Panel

Build a custom UI that shows only relevant controls:
# In a Panel Execute DAT

def onReady():
    ext = op('/daydream').ext.Daydream
    caps = ext.GetCapabilities()

    # Show/hide ControlNet sliders based on model
    available_controlnets = caps.get('controlnets', [])

    for cn in ['depth', 'canny', 'tile', 'openpose', 'hed', 'color']:
        slider = op(f'slider_{cn}')
        if slider:
            slider.par.display = cn in available_controlnets

    # Show/hide IP Adapter based on model
    ip_types = caps.get('ip_adapter_types', [])
    op('ip_adapter_panel').par.display = len(ip_types) > 0

Next Steps