Skip to content

Parameters

Reading Parameter Values

Always use .eval() to get a parameter's current runtime value:

# CORRECT — .eval() works in all modes (constant, expression, export, bind):
value = op('geo1').par.tx.eval()

# WRONG — .val only returns the constant-mode value:
value = op('geo1').par.tx.val

Setting Parameters

# These are equivalent:
op('geo1').par.tx = 5
op('geo1').par.tx.val = 5  # Also implicitly sets mode to constant

# Menu parameters accept both string name and index:
op('geo1').par.xord = 'trs'   # by name
op('geo1').par.xord = 5       # by index

Warning

Setting .val implicitly switches mode to CONSTANT. If the parameter was in expression mode, the expression is silently destroyed:

op('geo1').par.tx.val = 5  # Kills any active expression!

Toggle Parameters

Toggle parameters use 0/1, not "True"/"False":

# Via MCP set_parameter:
set_parameter(op_path="/project1/base1", par_name="active", value="1")

# In Python:
op('base1').par.active = True   # Works
op('base1').par.active = 1      # Also works

Type Casting

Direct method calls on parameter values require explicit .eval():

# CORRECT:
me.par.tx.eval().hex()

# WRONG — parameter objects don't have .hex():
me.par.tx.hex()

When passing values to standard Python functions, explicitly convert:

int(op('geo1').par.tx)
float(op('geo1').par.tx)
str(op('geo1').par.tx)

Creating Custom Parameters

Custom parameters are created via appendCustomPage() on COMPs:

page = comp.appendCustomPage('Controls')
pg = page.appendFloat('Speed', label='Speed')  # Returns ParGroup, NOT Par!
p = pg[0]                                       # Get the actual Par
p.default = 0.5
p.normMin = 0; p.normMax = 2    # Slider range
p.min = 0; p.clampMin = True    # Hard clamp

Important

All append* methods return a ParGroup (tuple-like), not a single Par. Always index with [0] for single-value parameters.

Available Types

page.appendFloat('Speed')      # Float parameter
page.appendInt('Count')        # Integer
page.appendToggle('Active')    # Boolean toggle
page.appendStr('Label')        # String
page.appendMenu('Mode')        # Dropdown menu (empty — set menuNames/menuLabels)
page.appendPulse('Reset')      # Fire-once button
page.appendRGB('Color')        # Creates Colorr, Colorg, Colorb
page.appendXYZ('Pos')          # Creates Posx, Posy, Posz
page.appendOP('Target')        # Operator reference
page.appendFile('Path')        # File path selector

Naming Rule

First letter MUST be uppercase, rest lowercase/numbers. No underscores. TD enforces this.

Cleanup

comp.destroyCustomPars()   # Remove ALL custom pars
par.Speed.destroy()        # Remove a single custom par

mod() for Module Access

The mod object accesses DAT modules without import — essential in parameter expressions:

# In a parameter expression (import not available):
mod.utils.myFunction()

# In a script (cache the reference for loops):
m = mod.utils
m.func()

# Access by path:
mod('/project1/utils').myFunction()

Operator Storage

Persistent data storage on any operator:

op('base1').store('count', 42)
val = op('base1').fetch('count', 0)  # 0 is default if missing
op('base1').unstore('count')

Warning

fetch() searches up the parent hierarchy by default. Use search=False for local-only:

op('base1').fetch('key', 0, search=False)

tdu.Dependency for Reactive Values

Wrap values so parameter expressions automatically recook when they change:

dep = tdu.Dependency(0)
dep.val = 5          # CORRECT — triggers recooks
dep = 5              # WRONG — destroys the Dependency object!

# For mutable contents:
dep.val = [1, 2, 3]
dep.val.append(4)    # Does NOT trigger update
dep.modified()       # Required — notifies dependents

tdu Utility Functions

tdu.clamp(val, min, max)
tdu.remap(val, fromMin, fromMax, toMin, toMax)
tdu.rand(seed)                     # Deterministic random [0.0, 1.0)
tdu.base('noise3')                 # 'noise'
tdu.digits('noise3')               # 3
tdu.validName('my op!')            # 'my_op_'
tdu.match('noise*', ['noise1'])    # ['noise1']