TouchDesigner Beginner Article 8

Replicators

Dynamically create and manage arrays of components from table data using the Replicator COMP.

⏱ 23 min replicators comps tables python instancing data-driven

The Replicator COMP is one of TouchDesigner’s most powerful tools for building data-driven visual systems. Instead of manually creating and configuring dozens of components, you describe your objects in a Table DAT and let the Replicator create and update them automatically. Add a row to the table, get a new component. Remove a row, the component disappears. Change a value, the component updates. This pattern scales from 5 instances to 500 without any additional wiring.

The Core Concept

The Replicator works with two things: a master component and a Table DAT. The master component is the template — a Container COMP, Base COMP, or any COMP that represents what one instance looks like. The Table DAT provides the data — one row per instance. The Replicator reads the table, creates one copy of the master component per row, and calls a Python callback that you write to configure each copy based on its row data.

This pattern is the foundation of generative composition: separate your data (the table) from your presentation (the master COMP) and let the system drive the rendering.

Setting Up the Table DAT

Create a Table DAT and give it column headers in the first row. For a layout of coloured circles, your table might look like this:

name x y hue radius
circle_01 0.15 0.7 0.0 0.08
circle_02 0.5 0.3 0.33 0.12
circle_03 0.8 0.6 0.66 0.06

The name column will become the name of each replicated component. The other columns are data that your callback will apply.

Create this table by double-clicking a Table DAT and typing directly, or populate it with Python:

t = op('instance_table')
t.clear()
t.appendRow(['name', 'x', 'y', 'hue', 'radius'])
import random
for i in range(12):
    t.appendRow([
        f'circle_{i:02d}',
        f'{random.uniform(0.05, 0.95):.3f}',
        f'{random.uniform(0.05, 0.95):.3f}',
        f'{i / 12.0:.3f}',
        f'{random.uniform(0.05, 0.14):.3f}'
    ])

Run this from a Script DAT or a CHOP Execute DAT when the project initialises.

Building the Master Component

Create a Container COMP and name it master. Enter it by double-clicking. Inside, build what one instance should look like:

  1. Place a Circle TOP with Radius set to 0.5 (we will override this from the table).
  2. Connect it to an HSV Adjust TOP with Hue Shift set to 0 (also overridden per instance).
  3. Connect to a Null TOP named OUT.

Back in the parent network, set the Container COMP’s resolution to 200×200 pixels. This master COMP is the template — the Replicator will make copies of it, and your callback will customise each copy.

Setting Up the Replicator COMP

Place a Replicator COMP in your network. Open its parameter dialog:

  • Master Operator: Set this to master (the Container COMP you just built).
  • Replicator Table: Set this to instance_table (the Table DAT with your data).
  • Name From: Set to Row Index or Column depending on whether you want TD to name replicas by index or pull the name from a specific column. Set to Column and specify the column name name.
  • Recreate All Pulse: Pressing this forces the Replicator to destroy and recreate all instances. Pulse it after changing the table structure.

The Replicator creates a copy of the master COMP for each data row, places them inside itself, and calls the onReplicatorPulse callback once for each copy.

Writing the Callback

Right-click the Replicator COMP and choose Edit Replicator Script. This opens a Text DAT with the callback template. The key function is onReplicatorPulse:

def onReplicatorPulse(replicatorOp, allOps, newOps):
    """Called when the replicator creates or updates instances."""
    table = op('instance_table')
    
    for i, replica in enumerate(allOps):
        # Row 0 is the header; data starts at row 1
        data_row = i + 1
        if data_row >= table.numRows:
            break
        
        # Read values from the table
        x = float(table[data_row, 'x'])
        y = float(table[data_row, 'y'])
        hue = float(table[data_row, 'hue'])
        radius = float(table[data_row, 'radius'])
        
        # Position the replica within a parent Container COMP
        # x and y are 0–1; convert to pixel offsets if needed
        replica.par.alignorder = i
        
        # Enter the replica and set its internal operator parameters
        circle = replica.op('circle1')
        if circle:
            circle.par.radius = radius
        
        hsv = replica.op('hsvAdjust1')
        if hsv:
            hsv.par.hueshift = hue

The allOps argument is a list of all replicated instances in creation order. newOps contains only instances created in the most recent pulse (useful for incremental additions). The loop reads each instance’s data row from the table and applies values to operators inside the replica.

Accessing Replicated Instances from Outside

From anywhere in the network, you can get a list of all active replicas:

replicas = op('replicator1').ops()
# Returns a list of all Component instances the replicator currently manages

# Access a specific one by name
circle = op('replicator1').op('circle_03')

This lets you update instances reactively — in a Frame Start Execute DAT, iterate all replicas and update their hue based on audio data or time.

Animating Replicas Independently

Each replica has its own internal network — and those operators can use expressions referencing their own parent index:

Inside the master COMP, set the Circle TOP’s Radius parameter to:

me.parent().par.alignorder * 0.01 + 0.04

This makes each instance slightly larger than the previous one, purely from its own position in the creation order — no Python needed.

For independent animation, add an LFO CHOP inside the master COMP. Set its Rate to:

me.parent().par.alignorder * 0.05 + 0.1

Each replica’s LFO now runs at a different rate, creating a staggered, rippling animation across all instances automatically.

The Full Example: 12 Coloured Circles

  1. Create instance_table Table DAT with 12 data rows (columns: name, x, y, hue, radius) using the Python snippet above.
  2. Create master Container COMP (200×200): Circle TOP → HSV Adjust TOP → Null TOP OUT.
  3. Create a parent Container COMP at full output resolution (e.g., 1920×1080). Place the Replicator COMP inside it.
  4. Set Replicator’s Master Operator to master, Replicator Table to instance_table.
  5. Write the onReplicatorPulse callback to position each replica and set hue/radius from table columns.
  6. Pulse Recreate All to generate 12 instances.
  7. Connect the parent Container COMP to a Null TOP named OUT.

You now have 12 independently-coloured circles laid out according to table data. Change any cell in the table and pulse the Replicator — the visuals update immediately. This is the key insight: your creative control is data in a table, not wires and parameters scattered across the network. The Replicator is how TouchDesigner scales from a single component to a system.