Python in TouchDesigner | Intro to Functions | TouchDesigner

Core Concepts

  • Functions as a concept
  • Anatomy of a function
  • Writing functions
  • Calling functions
  • Returning values
  • Passing arguments

Before we can tackle CHOP executes we need to take a moment to learn about functions. There’s a lot to learn about with functions, so we’re not going to dive too deep just yet… yet. We are, however, going to peer into this idea so we can better understand part of what we’ll see next as we move into the exciting world of executes.

Let’s start by looking at what a function actually is:

“A function is a block of organized, reusable code that is used to perform a single, related action. Functions provide better modularity for your application and a high degree of code reusing.

As you already know, Python gives you many built-in functions like print(), etc. but you can also create your own functions.”
Tutorials Point

Great! But… how can we better understand that? For a moment let’s first appreciate that we have a wide variety of functions that we do on a regular basis… we just don’t think of them as functions. Most of us know how to calculate a tip, or gas mileage, or estimate travel time, or pack a suitcase, or make lunch, or or or, and and and. We don’t think of these as functions, but if we had to write out very specific instructions about how to complete one of these tasks we’d actually be close to starting to wrestle with the idea of what a function is – it’s okay if that doesn’t make sense yet. Hang on tight, because we’re gonna get there.

Let’s first look at a simple example that examines the anatomy of a function. Next we’ll write a few simple functions. Then we’ll look at why that’s important when it comes to thinking about CHOP executes.

Starting with Anatomy.

Here we go, we’re going to write a dead simple function:

def first_function():
    print( 'Hello World' )


There we go. We did it. Now, if we were to run this in TouchDesigner, nothing would happen… so at first glance it would seem like we didn’t really write a function after all. That might be a good guess, but the reason nothing happened is because we never actually called our function, we just defined it – we wrote out all of the instructions, but we never asked TouchDesigner to actually run the function. To see anything happen, we need to actually call the function – we need to tell TouchDesigner that we need to run it. Let’s modify our example to see what that would look like.

def first_function():
    print( 'Hello World' )



Okay… time to take this all apart and see what makes it tick.

  • We’re started out by indicating that we were going to define a function… that’s really what we meant when we wrote “def.”
  • Next we gave that function a name, in our case we called it “first_function.”
  • Next we specified that we weren’t going to pass in any arguments or parameters by writing “()” – don’t worry, we’re going to learn more about that in a second.
  • Then we indicated that we were going to outline what was in the function with our “:”
  • The next line is indented one tab space and here we print out “Hello World”
  • We ended the function with a return statement, which in this case didn’t return anything.
  • Finally, we summoned our function into action by saying its name… well, writing its name “first_function()”

At this point we’ve written a very simple function that just prints out “Hello World.” We started with this simple example so we could just talk about its anatomy. Before we can move on to something a little more interesting, we need to unpack a few things. Specifically, we need to talk more about what it means to *return* something, and what an argument or parameter is when it comes to functions.

Let’s start with *return*. Like it’s name suggests, to return something is to give it back, or deliver something. Seems straightforward enough, right? We might imagine that sometimes we don’t want to print out the result of a function, but we do want to get something out the other side to use in another process. In this case, we want something returned to us after the function has run. Let’s look at that in a concrete way.

We’re going to use our same example first function, but make a few changes.

def first_function():
    text = 'Hello World'

    return text


Okay, here we can see that we changed our function so we don’t actually print out “Hello World” anymore, instead we return it at the end. If we run our function, we encounter our same problem that we saw earlier… it would seem as if nothing happened. What gives. Let’s change our function in one small way and see what we end up with:

def first_function():
    text = 'Hello World'

    return text

print( first_function() )

The small change to print out first_function() means that we’re now printing out what’s returned from this function. It might feel like a small difference, but it means that we’re able to control what comes out of our function when we summon it into action. That’s actually a very important thing, and we’ll see why shortly.

If we can control what comes out of our functions, surely we can control what goes into them… right? In fact, you are right.

Now that we now how to get something out of our function, let’s pass it some information do to something with. We’re going to write another simple function, this time to do some simple math.

def percent( val1 ):
    calculation = val1 * 0.01

    return calculation

print( percent( 50 ) )

Alright, what do we have here? Let’s imagine we want to change an integer into it’s float equivalent as a percentage. 50% as becomes 0.5, 10% would be 0.1, and so on and so on. Here we’ve written a function to do just that. In this case we’ve specified that our function accepts one argument which is named val1. We later see in our function that “calculation” is val1 * 0.01. Finally, we return calculation. This means we can give percent any number, and get a float value in return. Not bad.

Okay, let’s look at two more examples. Next we’ll write a simple function to calculate a tip based on a total bill. At the end of this we want to see our tip and our total bill – using our new found lingo, we’re going to return these values.

Okay, let’s make some Python magic happen. If you’re playing along at home, trying writing this yourself before you look at how I did it.

def tip_calculator( total , tip_percentage ):

    tip = total * ( tip_percentage / 100 )
    total_bill = total + tip
    return tip , total_bill

print( tip_calculator( 50 , 15 ) )

Here we want two things back, our tip and our total_bill. We start by calculating the tip, and then by adding that to our total. Finally we return these two values.

Let’s try one more idea on for size. This next time around you’re challenge is to use the function we just wrote, and to write another function as a compliment. This second function is going to print out these values to our text port so we can see them. By writing this as two separate functions we decide when we want to print out our results, and when we want to just return our tip and total_bill. As an extra challenge, see if you can write your new function to accept only a single argument.

Okay, let’s look at how you might solve this problem:

def tip_calculator( total , tip_percentage ):
    tip = total * ( tip_percentage / 100 )
    total_bill = total + tip
    return tip , total_bill

def display_total( tip_and_total_bill ):
    dotted_line = '- ' * 10
    tip_text = "Your total tip is {}"
    total_bill_text = "Your total bill is {}"
    print( dotted_line )
    print( tip_text.format( tip_and_total_bill[ 0 ] ) )
    print( total_bill_text.format( tip_and_total_bill[ 1 ] ) )
    print( dotted_line )


total = 100
tip_percentage = 20

print( tip_calculator( total , tip_percentage ) )

display_total( tip_calculator( total , tip_percentage ) )

How did you do? We can see that our first function stayed the same. Our second function accepts a single argument – tip_and_total_bill. This tuple (a series of values) is then used by our second function when printing out to our textport. This probably isn’t the best way to solve this problem… but for the sake of a simple example our chances of getting into trouble are pretty slim.

Okay, so why do all of this?! Well, let’s take a sneak peak at what’s coming next. If we look at the contents of a CHOP execute we see:

# me - this DAT
# channel - the Channel object which has changed
# sampleIndex - the index of the changed sample
# val - the numeric value of the changed sample
# prev - the previous sample value
# Make sure the corresponding toggle is enabled in the CHOP Execute DAT.

def offToOn(channel, sampleIndex, val, prev):

def whileOn(channel, sampleIndex, val, prev):

def onToOff(channel, sampleIndex, val, prev):

def whileOff(channel, sampleIndex, val, prev):

def valueChange(channel, sampleIndex, val, prev):

We should now recognize the contents of these DATs as functions… and not only are they functions, they’re functions with four named incoming arguments. Now we can really start to have fun.

Learn more about functions in Python

Download the sample files from github

Python in TouchDesigner | Data Structures – Dictionaries | TouchDesigner

Part 1 Core Concepts

  • Dictionaries – a structure and a concept
  • Looking at Dictionaries and Lists side by side
  • What are key value pairs
  • Retrieving values from dictionaries
  • Retrieving .keys() and .values()
  • Adding items to dictionaries
  • Nested data structures
  • A better text formatting approach with .format() (a big thank you to Willy Nolan for setting me on the right path with text formatting in Python 3)

Part 2 Core Concepts

  • Dictionaries – a structure and a concept
  • A practical look at dictionaries in TouchDesigner
  • Nested data structures – dictionaries in dictionaries
  • Using Dictionaries as a preset structure
  • Using Python to set parameters
  • Using Python variables in scripts to generalize our code

This isn’t the first time I’ve been on a tear about using Dictionaries in TouchDesigner – THP 494 & 598, Presets. That said, sometimes it’s easier to understand a concept if we back down a little bit and start from the beginning. With that in mind, let’s turn the speed down to 0.1 before we turn it up to 12 again.

Dictionaries are another type of data structure that we can use in Python. They’re similar to lists in that we can store information in them, and retrieve them easily. Dictionaries, however, are distinctly different from lists. Where lists are index based – which is to say that they have a specific order – dictionaries are key based.

What does that mean, and why do we care?! We think of dictionaries as being a pair of things a key, and a value. We might think of this as a name and its corresponding piece of information. Let’s look at something simple to get started. To get started let’s go back to our grocery example when we were talking about lists. In making a list for our trip to the grocery store we listed all of the items we needed from the store. We didn’t however, make any notes about quantity. Let’s quickly make that list again:

grocery_list = [ 'eggs' , 'milk' , 'bread' , 'butter' , 'coffee' ]

This is great, and it tells us lots of information, but maybe not all of the information we need. If I’m going to the store myself, this is a fine list. If I’m asking someone else to pick up these things for me, well then I need at least one other piece of information – quantity. If we’re using lists, we might do something clever, like make a list of lists with two items – the grocery item, and the desired quantity. That might look something like this:

grocery_list = [ 
    ['eggs' , '1 dozen' ] , 
    ['milk' , '1 pint' ] , 
    ['bread' , '2 loaves' ] , 
    ['butter' , '1 lb' ] , 
    ['coffee', '2 lbs' ]

This works fine, and might be a great way to hold onto this information. We can, however, use a dictionary to do this same thing. In this case we’re going to think of our grocery items as a keys, and quantities as values. Let’s look at what means:

grocery_list = {
    'eggs' : '1 dozen' , 
    'milk' : '1 pint' , 
    'bread' : '2 loaves' , 
    'butter' : '1 lb' , 
    'coffee': '2 lbs'

It’s important to note that I’ve used some indenting to make this easier to read, but another perfectly valid way to write this dictionary would be:

grocery_list = { 'eggs' : '1 dozen' , 'milk' : '1 pint' , 'bread' : '2 loaves', 'butter' : '1 lb' , 'coffee': '2 lbs' }

I just happen to think that anytime you can make something easier to read by a human, the better.

Okay, let’s talk about syntax here for a second. So the first thing we did was declare our dictionary as a variable. Next we used curly brackets to open our dictionary ( {} – these are curly brackets ). Next we wrote out our dictionary as key and value pairs separated by a colon – keys on the left, values on the right. Now in this example all of our values were strings, but they could just as easily have been integers, floats, booleans, lists, or even other dictionaries.

This is all well and good, but how do we get things out of our dictionary? We know how to retrieve things from a list, but a dictionary is a little different. When retrieving something from a dictionary we typically use a key. Let’s consider our first example again for a second. Let’s say we want to print out the quantity of eggs that we’re supposed to get from the store. We can do that like this:

print( grocery_list[ 'eggs' ] )

We can also retrieve the contests of dictionary with .keys() and .values():

print( grocery_list.keys() )
print( grocery_list.values() )

In both of these cases we get a list of keys or values.

It’s also important to know how to add items to our dictionary. There are a few ways to go about this, but let’s just look at one for now. We should start by creating an empty dictionary:

my_dictionary = {}

Now that we have an empty dictionary, we can add items to it. We do this by starting with the dictionary name, then placing the key in square brackets ([] these things), followed by an equal sign, and then what we want to be placed into the dictionary as the value. Let’s look at an example:

my_dictionary[ 'new_item1' ] = "cookies"

Okay, now that we’ve added one key value pair, let’s print out the keys and values in our list (to practice), and add a few more items:

my_dictionary = {}

my_dictionary[ 'new_item1' ] = "cookies"

print( my_dictionary.keys() )
print( my_dictionary.values() )

my_dictionary[ 'new_item2' ] = "cell_phones"

print( my_dictionary.keys() )
print( my_dictionary.values() )

my_dictionary[ 'new_item3' ] = 55

print( my_dictionary.keys() )
print( my_dictionary.values() )

my_dictionary[ 'new_item4' ] = [ 1 , 2 , 3 ]

print( my_dictionary.keys() )
print( my_dictionary.values() )

Wait… what did we just do there with item4?! Most of that should look pretty straightforward, and hopefully that makes sense for the most part. It is not, however, the most exciting part of using dictionaries. Dictionaries become the most exciting when we start to see how we can nest other lists or dictionaries inside of them.

Let’s look at a dictionary of mixed contents:

my_dictionary = { 
    "apple" : "these are delicious" , 
    "orange" : 12 , 
    "kiwi" : 55.5 , 
    "lots_of_things" : [
        "paper" , 
        "pens" ,
        44 , 

So we know how to get to the values associated with “apple” , “orange” , and “kiwi” , but how do we get to the contents of that list? Well, we can write something like this:

print( my_dictionary[ 'lots_of_things' ][ 0 ] )
print( my_dictionary[ 'lots_of_things' ][ 1 ] )
print( my_dictionary[ 'lots_of_things' ][ 2 ] )
print( my_dictionary[ 'lots_of_things' ][ 3 ] )

Here we see the same syntax that we use when retrieving list items.

That’s wonderful! So what about when we store dictionaries inside of dictionaries? Let’s look at a simple example. We can start by creating a dictionary with fruit’s as our keys. Each fruit will have a corresponding dictionary of quantity, origin, and if the fruit is organic. Okay, what would that look like:

my_dictionary_of_dictionaries = { 
    "apple" : {
        "quantity" : 10 ,
        "origin" : "Vermont" ,
        "organic" : True 
    } , 
    "orange" : {
        "quantity" : 20 ,
        "origin" : "California" ,
        "organic" : False
    } , 
    "kiwi" : {
        "quantity" : 26 ,
        "origin" : "Mexico" ,
        "organic" : False
    } , 
    "grapes" : {
        "quantity" : 50 ,
        "origin" : "Peru" ,
        "organic" : True

That’s pretty snazzy and all, but how do we pull things out of this data structure? We can follow the example we learned with lists, but instead of using index values, we can instead use keys. Let’s look at just apple to get started:

print( "Let's just look at apple" )
print( "quanitity -" , my_dictionary_of_dictionaries[ 'apple' ][ 'quantity' ] )
print( "origin -" , my_dictionary_of_dictionaries[ 'apple' ][ 'origin' ] )
print( "organic -" , my_dictionary_of_dictionaries[ 'apple' ][ 'organic' ] )

Here we can see that we start with our dictionary, with a key in square brackets, followed by another key in square brackets. Practice retrieving other keys – what about grapes, or oranges? Also practice by adding other keys inside of the fruit dictionaries. Don’t forget to pay careful attention to where you’ve placed your commas and, remember that keys are strings so they need quotation marks.

Once you’ve done that, let’s consider how we might use something like a dictionary here in TouchDesigner. We’re going to look something a little complex, but still relatively simple to help us get our bearings. Dictionaries can be a great help to us when we want to do things like creating save states. Let’s first think about what it would mean to set the properties of a text TOP with the contents of a dictionary.

Let’s start by making our dictionary. I’m going to use the same names for our dictionary keys that we find in our parameter names – just to make sure we know exactly where a value is going.

top_dictionary = { 
    "text" : 'monkey' , 
    "fontsizex" : 15 ,
    "alignx" : 1 ,
    "aligny" : 1 ,
    "fontcolorr" : 1.0 ,
    "fontcolorg" : 0.0 ,
    "fontcolorb" : 0.0 ,
    "fontalpha" : 1.0 ,
    "bgcolorr" : 0.0 ,
    "bgcolorg" : 0.0 ,
    "bgcolorb" : 0.0 ,
    "bgalpha" : 1.0

Now let’s flesh out our script to change the parameters of our TOP:

target_text = op( 'text1' )

target_text.par.text = top_dictionary[ 'text' ]

target_text.par.fontsizex = top_dictionary[ 'fontsizex' ]
target_text.par.alignx = top_dictionary[ 'alignx' ]
target_text.par.aligny = top_dictionary[ 'aligny' ]
target_text.par.fontcolorr = top_dictionary[ 'fontcolorr' ]
target_text.par.fontcolorg = top_dictionary[ 'fontcolorg' ]
target_text.par.fontcolorb = top_dictionary[ 'fontcolorb' ]
target_text.par.fontalpha = top_dictionary[ 'fontalpha' ]
target_text.par.bgcolorr = top_dictionary[ 'bgcolorr' ]
target_text.par.bgcolorg = top_dictionary[ 'bgcolorg' ]
target_text.par.bgcolorb = top_dictionary[ 'bgcolorb' ]
target_text.par.bgalpha = top_dictionary[ 'bgalpha' ]

That’s pretty great – but goodness that’s a lot of work just to change some settings. How might we think about using this idea to create a preset system? We’re not that far off form this idea at this point, so let’s dig in a little deeper. To really make this work, we need to revisit our dictionary. Specifically, we need to encapsulate our presets inside another layer. We need to make them their own dictionary as a set of values for another key. For example, we might want a named structure like “preset1” , “preset2” etc. to be how we retrieve settings. Let’s change our dictionary to make that happen:

top_dictionary = { 
    "preset1" : {
        "text" : 'monkey' , 
        "fontsizex" : 15 ,
        "alignx" : 1 ,
        "aligny" : 1 ,
        "fontcolorr" : 1.0 ,
        "fontcolorg" : 0.0 ,
        "fontcolorb" : 0.0 ,
        "fontalpha" : 1.0 ,
        "bgcolorr" : 0.0 ,
        "bgcolorg" : 0.0 ,
        "bgcolorb" : 0.0 ,
        "bgalpha" : 1.0
    } ,
    "preset2" : {
        "text" : 'pig' , 
        "fontsizex" : 80 ,
        "alignx" : 1 ,
        "aligny" : 0 ,
        "fontcolorr" : 0.0 ,
        "fontcolorg" : 0.0 ,
        "fontcolorb" : 1.0 ,
        "fontalpha" : 1.0 ,
        "bgcolorr" : 1.0 ,
        "bgcolorg" : 1.0 ,
        "bgcolorb" : 1.0 ,
        "bgalpha" : 1.0

Not bad. Now, how can apply these presets to our top? To do this we’re going to do one tricky thing. We’re going to write our scripts so that a python variable can stand in our first key. This will mean that we only need to change a single variable before re-running our script. That would look like this:

target_text = op( 'text1' )
dictionary_preset = 'preset2'

target_text.par.text = top_dictionary[ dictionary_preset ][ 'text' ]
target_text.par.fontsizex = top_dictionary[ dictionary_preset ][ 'fontsizex' ]
target_text.par.alignx = top_dictionary[ dictionary_preset ][ 'alignx' ]
target_text.par.aligny = top_dictionary[ dictionary_preset ][ 'aligny' ]
target_text.par.fontcolorr = top_dictionary[ dictionary_preset ][ 'fontcolorr' ]
target_text.par.fontcolorg = top_dictionary[ dictionary_preset ][ 'fontcolorg' ]
target_text.par.fontcolorb = top_dictionary[ dictionary_preset ][ 'fontcolorb' ]
target_text.par.fontalpha = top_dictionary[ dictionary_preset ][ 'fontalpha' ]
target_text.par.bgcolorr = top_dictionary[ dictionary_preset ][ 'bgcolorr' ]
target_text.par.bgcolorg = top_dictionary[ dictionary_preset ][ 'bgcolorg' ]
target_text.par.bgcolorb = top_dictionary[ dictionary_preset ][ 'bgcolorb' ]
target_text.par.bgalpha = top_dictionary[ dictionary_preset ][ 'bgalpha' ]

Alright. Looking closely at the above, we can see that we need only change the variable “dictionary_preset” in order to fetch a whole different set of values. Not bad, right?

Take some time to experiment with these ideas. As we head forward we’re going to start to look at how we can use executes and for loops to see how we can really start to make headway in using Python. We’ve laid a lot of ground work so we can really plow ahead.

Learn more about Python Data Structures

Download the sample files from github

Python in TouchDesigner | Data Strucutres – Lists | TouchDesigner

Part 1 Core Concepts

  • Lists – a structure and a concept
  • Creating lists – syntax and structure
  • Retrieving items from a list – Syntax
  • Adding items to a list .append() and .extend()
  • Lists of Lists

Part 2 Core Concepts

  • Lists – a structure and a concept
  • Why Lists matter in TouchDesigner
  • The Channel Class – seeing CHOPs as lists
  • The Point Class – thinking of geometry as lists
  • The COMP Class and .findChildren() – pulling apart returned lists
  • More about how to read the TouchDesigner wiki

Lists are the bees knees, they’re the cat’s pajamas, they’re almost better than sliced bread. There are a few important things for us to think about before we dive into the Python of lists. Python lists are just like the lists you might make on a piece of paper. They’re a sequential ordering of items. A grocery list might be:

  • eggs
  • milk
  • bread
  • butter
  • coffee

We often make lists, and while the order of our grocery list might be arbitrary, there are plenty of lists that are not. Frequently a todo list has a specific order:

  1. Have preliminary discussion with collaborators
  2. Check schedule for availability
  3. Block off time for new project
  4. Coordinate schedules
  5. Build a preliminary budget
  6. Draft contracts
  7. Confirm costs
  8. Book space
  9. Purchase equipment

While this is a silly example, the important consideration is that here you wouldn’t purchase equipment before you started a preliminary discussion with your collaborators. Of course that seems obvious – but remember that you have a sense of linearity, a sense of time, a sense of order, and a idiomatic frame that you subconsciously constructed based on the content of the list items. Alright, semiotics aside, the more important idea here is that lists have order. Now while that may seem obvious, we’ll see later that dictionaries don’t necessarily work in the way – and in fact this is an important distinction we need to make early on.

Let’s go back to our grocery list. What might that look like in Python?

grocery_list = [ 'eggs' , 'milk' , 'bread' , 'butter', 'coffee' ]

You’ll notice that our items are enclosed in matching foot or inch marks: ” or “”. We can remember back to our first lesson on printing that this helps us see that these are strings. That’s wonderful. What if we want to print the whole list? Well we can do this:

print( grocery_list )

That prints our whole list. That’s pretty swanky, but what if we just want a single item from our list? How can we just print that? Well, we’ll remember that 0 is still a number for us here in Python. That means the indexing of our list items looks like: 0 1 2 3 4

We can print a single item in our list by indicating the index of the item we want:

print( grocery_list[ 0 ] )

Let’s look at that a little more closely and print out all of the items we have in our list:

print( grocery_list[ 0 ] )
print( grocery_list[ 1 ] )
print( grocery_list[ 2 ] )
print( grocery_list[ 3 ] )
print( grocery_list[ 4 ] )

Let’s go one step further and really make that as explicit as possible – just to make sure we understand.

print( "The item in the 0 position of our list is %r" % grocery_list[ 0 ] )
print( "The item in the 1 position of our list is %r" % grocery_list[ 1 ] )
print( "The item in the 2 position of our list is %r" % grocery_list[ 2 ] )
print( "The item in the 3 position of our list is %r" % grocery_list[ 3 ] )
print( "The item in the 4 position of our list is %r" % grocery_list[ 4 ] )

We can make lists out of just about anything. Let’s make a list out of all of the data types we’ve talked about so far:

my_int_list = [ 1 , 2 , 3 , 4 ]
my_float_list = [ 1.235 , 1.5679 , 9.454 , 4.23485 ]
my_string_list = [ 'apple' , 'kiwi' , 'orange' , 'pineapple' ]
my_bool_list = [ True , True , False , True, False ]
my_mixed_list = [ 1.234 , 5 , 'apple' , True , 3.45 ]

One question we might have is how long is our list? Well, there happens to be an easy way for us to figure that out with len() – as in length.

len( my_int_list )

Practice printing the length of all of your lists.

We can also build lists from scratch. First we need to create an empty list.

my_list = []

print( 'As we go, we will print our list at each' )
print( 'step along the way' )
print( 'My List' , my_list )

Next we can add items to our list with .append( theValueOrStringToBeAddedHere ).

my_list.append( 1 )

# this is here to make a line break
print( '\n' )

print( 'So we just added a single number out our list' )
print( 'what does that look like now?' )
print( 'My List' , my_list )

We can even add multiple items at once with .extend( aListofItemsHere ):

my_list.extend( [ 45 , 2 , 100 , 6 ] )

# this is here to make a line break
print( '\n' )

print( 'Can we add multiple items at once?' )
print( 'My List' , my_list )

print( 'We sure can, we just need to use .extend' )
print( 'instead of .append' )

That’s great… but what does this mean for me in TouchDesigner? Well, in Touch many things are returned as lists. Samples in CHOPs are often a list, as are points in a SOP. Once we have a fundamental understanding of lists as a data structure we can start to really have a lot of fun.

Let’s look at CHOPs first.
First, make sure you add a noise CHOP to your network called noise1.

# define some variables
noise1 = op( 'noise1' )

# understanding the channel operator make
# a big difference in the way we use TouchDesigner
# lets start by just printing our variable

print( 'If we just print our noise1 variable we see this' )
print( noise1 )

print( 'If we print chan1 in noise1 one we see this' )
print( noise1[ 'chan1' ] )

print( 'we can also access this by using .chan( channelIndexHere )' )
print( noise1.chan( 0 ) )

print( 'Finally, we can see the whole list of values if we use' )
print( '.vals as we... that looks like') 
print( 'noise1.( channelIndexHere ).vals' )
print( noise1.chan( 0 ).vals )

That’s pretty fun… but let’s take that a step further.

# okay, but why do we care?

# define some variables
noise1 = op( 'noise1' )

# we can use what we've learned working with the .chans().vals
# to help us understand a little bit more about our CHOP
# for example, if our channel is a list of values, we can
# access those values just like we might in a list

print( noise1[ 'chan1' ][ 0 ] )
print( noise1[ 'chan1' ][ 1 ] )
print( noise1[ 'chan1' ][ 2 ] )

# we can even do the same things we might do in python here
print( len( noise1[ 'chan1' ] ) )

# though if we look at the wiki, we'll find that there's already
# a method to do just this called .numSamples
# and a method called numChans - which tells us how many channels
# If we think of our CHOP as a list of lists... then we can both
# see how many lists, and the length of the lists.

print( noise1.numSamples )
print( noise1.numChans )

Next let’s add a rectangle SOP to our network.

# define some variables
rectangle1 = op( 'rectangle1' )

# That's great... but what about geometry?
# Let's take a closer look at SOPs

print( 'Like with a sop we can print the path to rectangle1 operator' )
print( rectangle1 )

print( 'We can also look at the member .points' )
print( rectangle1.points )

print( 'Seeing that it is an object by itself, means we can look closer' )
print( 'What happens if we just ask for the first item in this object?' )
print( rectangle1.points[ 0 ] )

print( 'What if we ask to make the whole object a list, and the print it out?' )
print( list( rectangle1.points ) )

While CHOPs and SOPs seem like obvious operators that might have lists, they’re certainly not the only ones. The method .findChildren returns a list of operators when dealing with COMPs. Let’s take a closer look at that while we’re at it. I started by making a container and adding three buttons inside. Make sure that you look at the example file to see what I’ve done to get started.

# define some variables
radio_buttons = op( 'container_radio_buttons' )

# Let's take a look at findChildren
# we can see all of the ops inside of our container with:
print( radio_buttons.findChildren() )

# What if we only wanted to see the buttons??
print( radio_buttons.findChildren( depth = 1 ) )

# That's fine as long as there aren't any other operators
# inside of our conatiner. If we wanted to make sure we only
# got a list of buttons, we could be even more specific with

print( radio_buttons.findChildren( type = buttonCOMP , depth = 1 ) )

# Okay... so?
# Well, what we get back is a list, so what if we did this?

print( radio_buttons.findChildren( type = buttonCOMP , depth = 1 )[ 0 ] )

# Maybe we don't want to see the whole path, we just want to see it's name
print( radio_buttons.findChildren( type = buttonCOMP , depth = 1 )[ 0 ].name )

# Or maybe just its digits
print( radio_buttons.findChildren( type = buttonCOMP , depth = 1 )[ 0 ].digits )

# We could even click on one of our buttons this way
radio_buttons.findChildren( type = buttonCOMP , depth = 1 )[ 0 ].click()

Lists are powerful and also flexible data structures. And this is only the start of what we can do with them. Practice making some lists, accessing their contents, and printing out pieces of them.

Learn more about data structures in Python

Download the sample files from github

Python in TouchDesigner | Logic | TouchDesigner

Part 1 Core Concepts

  • Simple Logic constructs in Python and in TouchDesigner
  • If, And, Or
  • >= , <= , == , !=
  • if, else, elif

Part 2 Core Concepts

  • Simple Logic constructs in Python and in TouchDesigner
  • Scripting Logic and references
  • Comparing values
  • The Channel Class

Logical statements are profoundly helpful for us when we’re trying to convert an idea from what we understand, into something that a machine can interpret and act upon. Keeping that in mind, we need ways to distill ideas to their most fundamental pieces. What on earth do I mean? Well, we might think about ideas like greater than ‘>’ , less than ‘<‘ , equal to ‘==’ , and not equal to ‘!=’. Further, we need to consider how we indicate when something might happen. This this set of examples we’re going to focus on ‘if’ and ‘else’, as well as ‘if’ and ‘elif’ statements. What does all of that mean, well lets dig in and find out.

‘if’ and ‘else’ go hand in hand when we’re thing about logical operations. The broad concept is that we’re indicating what should happen if a particular condition is met, as well as what should happen when that condition isn’t met. If we were to write this out as a set of instructions what we’re really considering is what happens when a condition is met, and when it isn’t met. Often when thinking about these kinds of situations it’s easy for us to assume in action if our condition isn’t met, which is a fine human assumption, but less fine when working with a machine.

Let’s look at a simple let’s imagine you want to know when a number is equal to another number. This happens all the time when we’re programming, so we can start here.

First we need to do a bit of housekeeping and set ourselves up:

#deine a varaible
my_int1 = 5

Next we can start to look at the syntax of our logical test. Our tests starts with a lower case ‘if’ followed by the test. We end our statement with colon ‘:’ and indented on the next line we indicate what should happen if our test is met.

if my_int1 >= 6:
    print( 'This number is greater than or equal to 6' )

Now, we’re not done yet. We also need to indicate what should happen in all of the other circumstances… what happens if our number isn’t great than or equal to? For this we use ‘else:’, and indented on the next line we indicate what should happen in this circumstance.

    print( 'This number is less than or equal to 6' )

Okay, that means that the whole python party looks like this:

# define our variables
my_int1 = 5
# a simple logical test
if my_int1 >= 6:
    print( 'This number is great than or equal to 6' )

    print( 'This number is less than or equal to 6' )

That’s all well and good… most of the time. BUT, what if we don’t want to do anything if our test isn’t true? Surely there’s a way to handle that circumstance… right? There is indeed. We can use the ‘pass’ statement as a null operation. Meaning that nothing happens when we call pass. We can change the code above to instead be this:

# define our variables
my_int1 = 5

# a simple logical test
if my_int1 >= 6:
    print( 'This number is great than or equal to 6' )


That’s alright, but what if we want to live in more than an if else world? What if I want to explore a lot of possibilities? Well, one thing we might use is ‘elif’. ‘elif’ allows us to insert another if statement before we get to our final else. In English this might be something like, try this – did that work? If it didn’t, try this other thing. Did that work? Okay, then do this. Let’s look at what that might mean in Python:

# define our variables
my_int1 = 5

if my_int1 == 5:
    print( "This number is 5" )

elif my_int1 > 4 and my_int1 < 6:
    print( "This number is greater than 4, but less than 6" )

    print( "This number is less than 4 or greter than 6" )

That’s pretty great. But what if we want to test the same number twice? For example, what if I wanted to know if the number was greater than 4 and less than 6? How could I write that? In that case we could use ‘and’ in our logical test. Let’s look at what that might look like:

# define our variables
my_int1 = 5

if my_int1 > 4 and my_int1 < 6:
    print( "This number is greater than 4, but less than 6" )

    print( "This number is less than 4 or greater than 6" )

This this case, BOTH conditions must be met in order for our print statement to pass the test.

What about if I have a circumstance where I have two possible circumstances I want to be treated the same? What if I want to print out a line if my number is greater than 4 or if it is exactly equal to -5? In this case we might use ‘or’.

# define our variables
my_int1 = -5

if my_int1 > 4 or my_int1 == -5:
    print( "This number is greater than 4 or it's -5" )

    print( "This number is less than 4 or greter than 6" )

At this point surely you’re rolling your eyes thinking “this is all well and good for Python, but what does it mean in TouchDesigner?!” That’s an excellent question, and the magic here goes back to what we learned when thinking about references. Because Python variables can point to objects in TouchDesigner, we can move away from hardcoding our scripts, and instead let CHOPs stand in for our variables. For example, let’s say we want to compare two numbers, we can use references to CHOPs to do just this. First let’s make a constant CHOP with two channels, then we can write this bit of Python:

# define our variables
my_int1 = op( 'constant1' )[ 'chan1' ]
my_int2 = op( 'constant1' )[ 'chan2' ]

if my_int1 == my_int2:
    print( "These values are equal!" )

elif my_int1 > my_int2:
    print( "Integer 1 is greater than integer 2" )

elif my_int1 < my_int2:
    print( "Integer 1 is less than integer 2" )

This is still the tip of the iceberg as it were. The fun stuff starts to happen in a few more lessons. I know that might feel frustrating, but stick with me. The better our understanding of the fundamentals and printing, the happier we’ll be when it comes time to do the fancier foot work of making our networks hum. We need to know a little more about data structures first, and then we’ll start to get to the magic of executes… and loops. Then we’ll really be screaming along. Before you know it you’ll be writing whole functions, and then using them as modules. It’s all coming – so hang on tight.

Find Python lessons at Learn Python the Hard Way

Download the sample files from github

Python in TouchDesigner | Writing Python References | TouchDesigner

Core Concepts

  • Python expressions as references
  • Syntax
  • Accessing CHOPs
  • Accessing DATs
  • How to read the python help on the Derivative Wiki

References are one of the engines of TouchDesgner. They’re how we connect elements, and move information between operator families (and many other things as well.) I’ve written a good chunk already about understanding references – check out understanding referencing, and understanding referencing part 2 – so I’m not going to do my best not to revisit all of that same information.

I do, however, want to look at referencing from the stand point of Python. The most important thing we might do is understand the anatomy of a python expression used in as a reference to another operator. Let’s start with a simple example. Let’s say I have an LFO CHOP (let’s say that it’s lfo1, and has a single channel, chan1) that’s oscillating between -0.5 and 0.5. I want to use this changing number to alter the position of a circle. In the Center X parameter of the circle I can use the following expression:

op( 'lfo1' )[ 'chan1' ]

Here I’ve specified that I’m looking at an operator. I know this because I started my expression with:

op( )

Inside of that I’ve placed the string name – remember that we now know it’s a string as we can see that it’s encased in quote marks – of the operator:

op( 'lfo1' )

Next I’ve indicated which channel in that operator I want to use – [ ‘chan1’ ]:

op( 'lfo1' )[ 'chan1' ]

We’ll notice that this name is inside of square brackets. In the case above, we referenced the channel we were interested in by using the name of the channel. We could also have used the expression:

op( 'lfo1' )[ 0 ]

In this case we’ve accessed the same channel, but by using it’s index rather than it’s name. We might imagine that our channels are like good little school children heading out to recess all in a line. These lovely kiddos both have a name, and an order that they occupy in the line.

So why 0?

In Python, and in fact in many programming languages, 0 is still used as a number that represents a place. 0 is the first item in a sequence. We’re accustomed to thinking of numbers as quantities and not as distances or sequences. If we think of this in terms of distance rather than quantity it makes a little more sense. If we’re 1 mile (or kilometer) away from our destination, we’re not there yet… we still have one more mile (or kilometer) to travel. This takes a bit of getting used to, but the longer you work with any programming language the more this will make sense.

Alright. This is great, so let’s think about a table of information as well while we’re here.

Sample Table

0 1
0 Color Quantity
1 red 10
2 blue 5

Above we have a table that exposes the index values for the rows and columns. Here we can see that the word “Color” is in row 0, column 0. That will become important here in just one moment. Let’s imagine that our table is called “marbles.” We’d like to write a reference to the quantity of red marbles. We can access the contents of this cell several ways. Just like with Channel Operators, in DATs we can use the index values of our table, or we can use the header names. Let’s look at what that means.

Using the name of the row and column we could write the following reference:

 op( 'marbles' )[ 'red' , 'Quantity' ]

We could also write this expression by using only the index values of the table:

 op( 'marbles' )[ 1 , 1 ]

Finally, we can also mix and match these approaches and use either of these expressions for the same result:

 op( 'marbles' )[ 1 , 'Quantity' ]
 op( 'marbles' )[ 'red' , 1 ]

Download the sample files from github

Python in TouchDesigner | Variables | TouchDesigner

Core Concepts

  • Understanding variables in Python (different form what we mean with TouchDesigner variables)
  • Referencing by using variables in a script
  • simple scripting
  • simple preset recall

There are lots of resources on the web that describe variables better than I might:

The essential idea here, however, is that you have something that you want to reference by name. That something might be a sentence, it might be a quantity, it could be anything really. Again, it’s more important for us in this moment to consider that our something (whatever it is) happens to be a piece of information that we want re-use.

Let’s look at a dead simple example, to help us get started. If variables aren’t new to you feel free to skip ahead.

Let’s imagine you own a toy store. That toy store happens to sell marbles. That’s great, good for you – you’re a marvelous little capitalist. Now, let’s imagine that you want to do an inventory of all of your marbles. You have several different varieties of marbles, and you’d like to be able to think of them as different, while also having a total count. In this situation we might keep track of your marbles by using some variables:

red_marbles = 10
blue_marbles = 5
green_cat_eyes = 6
blue_cat_eyes = 12

Nice work. Now, we can print out each one of those, and get back our stored quantity. We could also do something like this:

total_marbles = red_marbles + blue_marbles + green_cat_eyes + blue_cat_eyes

Now we also know the total quantity of marbles. Super. Finally, we might want to see all of that. Let’s look at what that might look like:

print( "Currently in your inventory you have:" )
print( "%d red marbles" % red_marbles )
print( "%d blue marbles" % blue_marbles )
print( "%d green cat eye marbles" % green_cat_eyes )
print( "%d blue cat eye marbles" % blue_cat_eyes )
print( "-" * 10 )
print( "That makes for %d total marbles" % total_marbles )

That’s great, and hopefully you’re a careful shop keeper and you don’t loose any of your marbles… it was a long set-up for that bad joke.

What does this do for us here in TouchDesigner? When we’re scripting in Touch it’s often useful to be able to assign variables for all sorts of things. This especially useful when referencing operators.

Let’s quickly consider one example. We might, have a level TOP that we want to make changes to. Starting with a simple task, lets imagine we want to use a script to change the opacity of a level TOP to 0. We could easily write something like this to solve this need:

op( 'level1' ).par.opacity = 0

That’s short and simple and gets the job done. Love it. Now, let’s imagine a slightly more complicated world where I want to change lots of parameters for this operator. I want to change the invert, black level, brightness 1, gamma 1, contrast, and opacity. That’s great. Let’s write all of that out and see what we end up with:

op( 'level1' ).par.invert = 0.31
op( 'level1' ).par.blacklevel = 0.27
op( 'level1' ).par.brightness1 = 1.45
op( 'level1' ).par.gamma1 = 0.5
op( 'level1' ).par.contrast = 1.76
op( 'level1' ).par.opacity = 0.782

That’s not too bad, but we could make that a little less error prone if we were to simplify some of our script:

level = op( 'level1' )

level.par.invert = 0.31
level.par.blacklevel = 0.27
level.par.brightness1 = 1.45
level.par.gamma1 = 0.5
level.par.contrast = 1.76
level.par.opacity = 0.782

That’s pretty swanky, but let’s imagine a situation where I’ve made a table full of presets that I want to be able to reference. Let’s look at how we might tackle something like that:

# define our variables:
presets = op( 'table_presets' )
level = op( 'level1' )
row_ref = 'preset1'

# change some parameters
level.par.invert = presets[ row_ref , 'invert' ]
level.par.blacklevel = presets[ row_ref , 'blacklevel' ]
level.par.brightness1 = presets[ row_ref , 'brightness1' ]
level.par.gamma1 = presets[ row_ref , 'gamma1' ]
level.par.contrast = presets[ row_ref , 'contrast' ]
level.par.opacity = presets[ row_ref , 'opacity' ]

Okay… so what happened here? First we defined created a variable called “presets” that stands in for op( ‘table_presets’ ). We also made one called “level” and one called “row_ref”.

Next we wrote a generalized set of instructions to change some parameters using our variables. For the sake of seeing it all written out let’s look write it out long-form:

op( 'level1' ).par.invert = op( 'table_presets' )[ 'preset1' , 'invert' ]
op( 'level1' ).par.blacklevel = op( 'table_presets' )[ 'preset1' , 'blacklevel' ]
op( 'level1' ).par.brightness1 = op( 'table_presets' )[ 'preset1' , 'brightness1' ]
op( 'level1' ).par.gamma1 = op( 'table_presets' )[ 'preset1' , 'gamma1' ]
op( 'level1' ).par.contrast = op( 'table_presets' )[ 'preset1' , 'contrast' ]
op( 'level1' ).par.opacity = op( 'table_presets' )[ 'preset1' , 'opacity' ]

This works just the same… so why use variables. Well, in this case I used variables to keep my code a little more tidy. I also did this because it means I’m less likely to make an error if I’m using shorter names. Most importantly, we did this because we’ve now created a variable called row_ref. This means we can change how this script works, just by altering this single variable. Let’s say that we have two different presets. It would be far less fun to write the same set of scripts all over again just to reference a different preset. Instead, we can just change our variable to indicate which preset to use. That means that by making this single change:

row_ref = 'preset2'

We’ve actually made this change:

op( 'level1' ).par.invert = op( 'table_presets' )[ 'preset2' , 'invert' ]
op( 'level1' ).par.blacklevel = op( 'table_presets' )[ 'preset2' , 'blacklevel' ]
op( 'level1' ).par.brightness1 = op( 'table_presets' )[ 'preset2' , 'brightness1' ]
op( 'level1' ).par.gamma1 = op( 'table_presets' )[ 'preset2' , 'gamma1' ]
op( 'level1' ).par.contrast = op( 'table_presets' )[ 'preset2' , 'contrast' ]
op( 'level1' ).par.opacity = op( 'table_presets' )[ 'preset2' , 'opacity' ]

This is only the tip of the iceberg, but helps us see how useful using variables in Python can be.

Download the sample files from github

Python in TouchDesigner | Printing | TouchDesigner

Core Concepts

  • Using the text port
  • Running scripts
  • print()
  • strings, integers, floats, and booleans
  • printing and joining
  • simple substitution in strings

Printing out lines isn’t especially interesting on the face of it. That being said, this is one of the most powerful places to get your bearings. I almost always start any python related task by printing out bits of pieces of what I’m up to. It lets me see into the otherwise invisible process of code execution. Many folks will swear by this or that debugger, but at the end of the day your best debugging tool is just printing out what’s happening. Learning the ins and outs of printing will also transfer to a number of different bits and pieces along the way. We’ll start here as a way to get a solid handle on a few basic elements.

Download the sample files from github

Advanced Instancing | Puzzle Pieces | TouchDesigner

Part 1

Core Concepts

  • Instancing geometry
  • Replicators and Clones
  • The Sort SOP
  • Images composed out of component pieces
  • Real time rendering

Part 2

Core Concepts

  • Instanced geometry from pixel data
  • Texture Arrays – the 3D Texture TOP
  • The Sort SOP
  • Images composed out of component pieces
  • Real time rendering

Advanced Instancing | Instancing with the Animation COMP | TouchDesigner

Once upon a time this had an audio track, and then suddenly it didn’t. A fix coming by the end of the week. Sorry for the delay.

Core Concepts

  • Instancing geometry
  • Working with the Animation COMP
  • Building Animation Channels
  • The Shuffle CHOP
  • Real time rendering