DATs: Data Operators
Use Data Operators to manage text, tables, and Python scripts, and fetch live data from web APIs to drive parameters.
Data Operators — DATs — deal in text, tables, and executable code. Where TOPs process images and CHOPs process numbers, DATs process information: raw strings, structured tabular data, Python scripts, JSON payloads, and GLSL shader code. DATs are what make TouchDesigner a programmable, data-driven environment rather than just a visual patching tool.
The Text DAT
A Text DAT holds any plain text. Double-click it to open an editor. What you put in there depends on context: it might be a Python script, a chunk of GLSL, a list of values, or just a label for documentation.
The Text DAT becomes a Python script when you reference it in a Script TOP, Script CHOP, or Execute DAT. You can also use it to store GLSL code and link it into a GLSL TOP or GLSL MAT via the Pixel Shader DAT parameter.
The Table DAT
A Table DAT organises data into rows and columns, like a spreadsheet. Each cell contains a string value. The first row is typically used as column headers.
You can edit a Table DAT manually by double-clicking it, or populate it programmatically. Tables are the backbone of data-driven systems — a Replicator COMP, for example, reads a Table DAT to know how many copies to create and what parameters to give each one.
Reading a Table DAT in Python:
# Inside any Python expression or Script DAT
table = op('table1')
# Get a cell value by row and column name
value = table['my_row', 'my_column']
# Get a cell by row index and column index
value = table[2, 1]
# Get the number of rows
num_rows = table.numRows
# Iterate all rows
for row in range(table.numRows):
x = float(table[row, 'x'])
y = float(table[row, 'y'])
Note that all Table DAT values are strings — convert to float() or int() when using them numerically.
You can also write to a Table DAT from Python:
table = op('table1')
table.clear()
table.appendRow(['name', 'x', 'y', 'hue']) # header
table.appendRow(['circle_01', '0.2', '0.5', '0.1'])
table.appendRow(['circle_02', '0.7', '0.3', '0.6'])
The Execute DAT
A Execute DAT runs Python code in response to events. Open its parameter dialog and you will see a series of toggle parameters: Off to On, On to Off, Value Change, Frame Start, Frame End. Enable the ones you need and write the corresponding Python function in the DAT’s text.
The most commonly used callback is onFrameStart:
def onFrameStart(frame):
# This runs every frame
val = op('audio_bass')['bass']
op('geo1').par.ty = val * 2.0
This is more efficient than putting the same logic in a parameter expression for cases where you need to update multiple parameters at once or do conditional logic.
The CHOP Execute DAT
A CHOP Execute DAT specifically reacts to changes in a CHOP’s channels. Point its CHOP parameter at a CHOP and enable the Value Change callback. Now every time the CHOP’s value changes, your Python runs.
def onValueChange(channel, sampleIndex, val, prev):
print(f"Channel {channel.name} changed from {prev:.3f} to {val:.3f}")
if channel.name == 'done' and val == 1:
op('window1').par.winopen.pulse()
This pattern is how you react to a Timer CHOP finishing, a beat detector firing, or a threshold being crossed — the CHOP Execute DAT bridges the numerical CHOP world into the event-driven Python world.
The Web Client DAT
The Web Client DAT sends HTTP requests and stores the response. Set its URL parameter to any REST API endpoint. Set Method to GET, POST, PUT, or DELETE as needed. If the server requires headers or a body, add them in the Request Headers and Request Body parameters. Pulse the Submit parameter (or call op('webclient1').par.submit.pulse() from Python) to fire the request.
The response body lands in the Web Client DAT’s text as a string. For JSON APIs, that string is a raw JSON object — you then need a JSON DAT to parse it.
The JSON DAT
Connect a Web Client DAT (or any Text DAT containing JSON) to a JSON DAT. The JSON DAT parses the input and converts it to a Table DAT format you can read. For a simple flat JSON object, each key becomes a column header and the value is in row 0.
For nested JSON, the JSON DAT’s Path parameter lets you navigate into sub-objects using JSON Pointer syntax. For example, if your weather API returns {"current": {"temp": 21, "humidity": 60}}, setting Path to /current gives you a table with temp and humidity columns.
Putting It All Together: Live Weather Data
Here is a complete example that fetches live weather data from a public API and uses it to drive a visual parameter.
- Place a Web Client DAT. Set URL to
https://wttr.in/?format=j1(a public, no-auth weather JSON API). Set Method to GET. - Add a JSON DAT after it. Point its input at the Web Client DAT. Set Path to
/current_condition/0to navigate into the first element of the current_condition array. - Now you have a Table DAT with columns like
temp_C,humidity,windspeedKmph. Place a Table DAT after the JSON DAT (or just read the JSON DAT directly). - Add an Execute DAT. Enable
Frame Start. In theonFrameStartfunction, read the temperature column and map it to a colour temperature in your visuals:
def onFrameStart(frame):
# Only update every 300 frames (~5 seconds at 60fps)
if frame % 300 != 0:
return
op('webclient1').par.submit.pulse()
def onFrameEnd(frame):
try:
temp = float(op('jsondat1')['0', 'temp_C'])
# Map temperature 0–40°C to hue 0.67 (blue) to 0.0 (red)
hue = 0.67 - (temp / 40.0) * 0.67
op('hsvAdjust1').par.hueshift = hue
except:
pass
- Wire your HSV Adjust TOP into the network and name it
hsvAdjust1.
Now your visual hue shifts from blue to red depending on the real-world temperature outside, updated every five seconds. This is the fundamental pattern for any data-driven installation: fetch → parse → map → apply.
DATs feel abstract until you need them — then you need them constantly. Learn to read Table DAT cells from Python early, and the rest of the data-routing tools will click into place naturally.