TouchDesigner Expert Article 10

Publishing & Client Handoff for TouchDesigner Projects

Package, document, and hand off a professional TouchDesigner installation with consolidated assets, a custom Perform UI, automated startup, remote monitoring, and full client documentation.

⏱ 55 min handoff deployment documentation monitoring client

The last 20% of a TD project — packaging, handoff, and long-term support — is where most technical directors underinvest. A client who can’t restart the installation without calling you is not a successful deployment. A show file that only runs on your development laptop is not a deliverable. This article covers the full handoff process.

Consolidating Assets

TD’s File > Consolidate Assets (or File > Backup > Consolidate) copies all externally referenced files — movies, audio, GLSL files, Python modules, images, .tox operators — into the project directory. Run this before every delivery so the project is self-contained.

After consolidation, verify by temporarily renaming the original asset directories and relaunching TD. Any missing file references will show as errors in the console. Common items that consolidation misses:

  • Absolute paths hardcoded in Python scripts (/data/my_video.mov instead of project.folder + '/assets/my_video.mov')
  • Files referenced inside .tox component files that were built on a different machine
  • Python packages imported at module level from system-wide site-packages rather than TD’s bundled packages

Fix absolute paths before consolidation by using TD’s built-in path helpers:

# Correct — relative to .toe file
video_path = project.folder + '/assets/videos/intro.mp4'

# Also correct — using app.samplesFolder for TD's built-in samples
font_path = app.samplesFolder + '/Fonts/ClearSans-Regular.ttf'

# Wrong — breaks on any machine that isn't yours
video_path = 'C:/Users/username/Documents/MyProject/assets/intro.mp4'

Documenting Custom .tox Operators

Any .tox you hand off should be self-documenting. Open the .tox’s component editor, go to the Custom Parameters page, and add a Help page:

  1. Right-click any parameter → Add Parameter → choose type
  2. In the parameter’s page properties, add a Help DAT reference containing parameter descriptions
  3. Use the component’s Info section to document inputs, outputs, and expected cook behaviour

For complex operators, write an Info CHOP channel list that describes what each output channel means — this appears in TD’s Info popup when the operator is selected:

# In a CPlusPlus CHOP or Script CHOP info callback
def getInfoCHOPChan(index):
    descriptions = [
        ('status',     'Connection status: 1=connected, 0=disconnected'),
        ('latency_ms', 'Round-trip network latency in milliseconds'),
        ('packets_rx', 'Total packets received since startup'),
    ]
    if index < len(descriptions):
        return descriptions[index]

Writing a Technical Rider

A technical rider is the hardware specification document that the venue or client must satisfy before install day. It is not a wish list — it is a contract. Include:

TECHNICAL RIDER — [Project Name] v1.2
Date: [Date]

COMPUTING
  GPU:      NVIDIA RTX 4080 (16 GB VRAM) or equivalent
            Acceptable alternatives: RTX 4090, RTX A4000, RTX A5000
            NOT acceptable: AMD GPUs (CUDA dependencies), any <12 GB VRAM card
  CPU:      12-core Intel Core i9-13900K or AMD Ryzen 9 7950X
  RAM:      64 GB DDR5 (minimum 32 GB DDR4)
  Storage:  500 GB NVMe SSD (Samsung 970 Pro or equivalent)
  OS:       Windows 11 Pro 22H2, clean install, no antivirus, no automatic updates
  TD Build: TouchDesigner 2023.11600 (exact build required — not newer, not older)

NETWORKING
  Dedicated gigabit switch (unmanaged or IGMP-snooping only)
  No internet connection during show — offline only
  Fixed IP: 192.168.1.10 (master), .11, .12 (slaves)

DISPLAY
  Output connector: DisplayPort 1.4 (adapters to HDMI 2.1 acceptable, not DP 1.2)
  Target display: [Projector model and keystone corrections pre-calibrated by venue]

POWER
  UPS for each machine: minimum 15-minute runtime at 1200W peak draw
  All machines on same phase to avoid ground loops

Building a Perform-Mode UI

The Perform UI is what a non-technical operator sees and uses during the show. Design it for clarity over density:

# In the Perform UI Base COMP — create large, clearly labelled controls

class PerformUI:
    def __init__(self, owner):
        self.owner = owner

    def build_ui(self):
        """Called once to construct the Perform Mode UI layout."""
        # Use Container COMP with Button COMPs and Slider COMPs
        # Each button gets a meaningful label and confirmation dialog for destructive actions
        pass

    def on_next_cue(self):
        """Called by NEXT CUE button."""
        op('/project1/cue_system/cue_runner').go(None)
        # Visual feedback: flash the button green briefly
        btn = self.owner.op('btn_next_cue')
        btn.par.Bgcolor1r = 0.2
        btn.par.Bgcolor1g = 0.8
        btn.par.Bgcolor1b = 0.2
        run("args[0].par.Bgcolor1r = 0.15; args[0].par.Bgcolor1g = 0.15; args[0].par.Bgcolor1b = 0.15",
            btn, delayFrames=10)

    def on_emergency_black(self):
        """Emergency blackout — mutes all output immediately."""
        op('/project1/output/master_level').par.Value0 = 0.0
        print(f'[PERFORM] Emergency black at {absTime.seconds:.1f}s')

Design principles for the Perform UI:

  • Minimum button size: 120×60 pixels — operators work in the dark with adrenaline
  • Colour code by function: green = go/advance, red = blackout/stop, yellow = warning states
  • Show current cue name and next cue name prominently
  • Add a hardware health summary: GPU temp, cook fps, any disconnected sources
  • Include an emergency blackout button that requires no confirmation

Remote Monitoring via Web Server DAT

The Web Server DAT (Operators > DAT > Web Server) lets you serve a status page accessible from any browser on the local network — useful for show technicians monitoring from FOH or a backstage tablet.

# In the Web Server DAT's Callbacks DAT

def onHTTPRequest(webServerDAT, request, response):
    if request['uri'] == '/status':
        import json

        status = {
            'timestamp': absTime.seconds,
            'fps': round(1.0 / max(me.cookTime, 0.001), 1),
            'current_scene': op('/project1/master_controller').par.Sceneindex.val,
            'gpu_temp': get_gpu_temp(),
            'ndi_sources': check_ndi_sources(),
            'data_age_seconds': absTime.seconds - op('data_store')['last_update'],
        }

        response['statusCode'] = 200
        response['statusReason'] = 'OK'
        response['data'] = json.dumps(status)
        response['headers'] = {'Content-Type': 'application/json',
                                'Access-Control-Allow-Origin': '*'}
        return response

    elif request['uri'] == '/':
        # Serve a simple HTML dashboard
        response['statusCode'] = 200
        response['data'] = build_dashboard_html()
        response['headers'] = {'Content-Type': 'text/html'}
        return response

def get_gpu_temp():
    import subprocess
    result = subprocess.run(['nvidia-smi', '--query-gpu=temperature.gpu',
                             '--format=csv,noheader'], capture_output=True, text=True, timeout=2)
    return int(result.stdout.strip()) if result.returncode == 0 else -1

Set the Web Server DAT to port 8080, start it in a startup Execute DAT, and give the client the URL http://[machine-ip]:8080 for the dashboard.

Automated Startup

Create a Windows Task Scheduler task that launches TD on system boot:

@echo off
REM td_launch.bat — place in C:\Show\

REM Wait for Windows to finish booting (GPU drivers, etc.)
timeout /t 30 /nobreak >nul

REM Set machine role environment variable
set TD_MACHINE_ROLE=master

REM Launch TD in Perform mode, load the show file
"C:\Program Files\Derivative\TouchDesigner\TouchDesigner.exe" ^
    "C:\Show\show.toe" ^
    --perform ^
    --fullscreen ^
    --monitor 1

REM If TD exits (crash or deliberate), wait and relaunch
timeout /t 5 /nobreak >nul
goto :restart_label

:restart_label
echo [%date% %time%] Relaunching TouchDesigner...
"C:\Program Files\Derivative\TouchDesigner\TouchDesigner.exe" ^
    "C:\Show\show.toe" ^
    --perform ^
    --fullscreen ^
    --monitor 1

In Task Scheduler: create a new task, trigger = At Startup, action = run C:\Show\td_launch.bat, run with highest privileges, check “Run whether user is logged on or not”.

Version Archiving with Git LFS

TD’s .toe files are binary — standard Git diffs are useless for them. Use Git LFS (Large File Storage) to version binary assets while keeping your repo functional:

# One-time setup
git lfs install
git lfs track "*.toe"
git lfs track "*.tox"
git lfs track "*.mov"
git lfs track "*.mp4"
git lfs track "*.wav"
echo "*.glsl" >> .gitattributes  # Text GLSL files — normal git tracking is fine

git add .gitattributes
git commit -m "Add Git LFS tracking for binary TD files"

For installations without internet access (no GitHub), use folder-based versioning with a Python script that creates timestamped archives:

# archive_version.py — run manually before each show day
import shutil, datetime, os

project_dir = r'C:\Show'
archive_dir = r'D:\Show_Archives'
timestamp = datetime.datetime.now().strftime('%Y%m%d_%H%M')
dest = os.path.join(archive_dir, f'show_{timestamp}')
shutil.copytree(project_dir, dest)
print(f'Archived to {dest}')

The Complete Handoff Package

Deliver as a USB drive or network share with this structure:

SHOW_HANDOFF_v1.2/
  show/                          ← Consolidated TD project
    show.toe
    assets/
    td_launch.bat
    td_watchdog.bat
  documentation/
    technical_rider.pdf
    quick_start_guide.pdf        ← 2-page laminated card for operators
    network_diagram.pdf
    parameter_guide.pdf          ← What each knob/slider does
  installers/
    TouchDesigner_2023.11600.exe
    NVIDIA_Driver_537.13.exe
    NDI_Tools_5.5.exe
  monitoring/
    status_url.txt               ← http://192.168.1.10:8080
  video_walkthrough/
    01_startup_procedure.mp4     ← 5-minute screen recording
    02_daily_operation.mp4
    03_troubleshooting.mp4

The video walkthrough is the single most valuable piece of documentation — a 5-minute recording of you operating the show, talking through what you’re doing and why, is worth more than 20 pages of written documentation for a non-technical client. Record it in Perform Mode showing the actual UI they will use. Cover the three most common failure cases (NDI source drops, the show file won’t open, the wrong monitor is full-screen) with on-camera solutions.

The quick_start_guide.pdf is a two-page laminated card that lives at the installation’s control point permanently. It contains: startup procedure (numbered steps with screenshots), shutdown procedure, one emergency contact number, and the three most common problems with their solutions. Nothing more — brevity is the point.