Two Tricks All TouchDesigner Beginners Should Know by Elburz Sorkhabi
Hi there! You’re probably expecting to see Zoe here, but you’re stuck with me, Elburz, instead! Zoe is a growing powerhouse in the interactive tech and immersive media scene and a great asset to the community when it comes to sharing knowledge and experiences. We thought it would be a fun thing to do guest post on each other’s blogs. So here’s my guest post that I hope you enjoy and find a lot of value in.
TouchDesigner is an amazing environment for creating artistic media. But it’s not all sunshine and rainbows! There is so much to learn that it can feel overwhelming for new users. On top of that, there are new features constantly coming out, so even pros have a tough time keeping up sometimes. With all that said, there are two techniques that are really easy to use and provide a lot of power and flexibility for developers. These are Global OP Shortcuts and delaying Python scripts. So let’s dive in!
Global operator shortcuts
Global operator shortcuts are a great way to access important data from different areas of your network. As you work on larger and larger projects, you’ll begin to find the need to organize and compartmentalize all the different areas and features of the installation you’re building. You quickly find accessing data isn’t as easy as dragging a wire around. You start using more and more select operators and have to start remembering longer and longer paths that lead to important data operators (like the output of Kinect sensors or similar).
Something that can save a ton of time in this regard are global operator shortcuts. These only work on components but they can make pathing to important areas extremely easy. For example if you have a Null CHOP operator named KINECT_DATA a few levels deep in your network, something like:
Accessing this data regularly might become a bit of a pain, and what happens in the off chance that you need to move that Kinect component somewhere else? Dun dun dunnnnnnnnnnnnnnnnnn, broken paths! What you can do to simplify this and make your projects more resilient to bugs is to use global op shortcuts.
To create an operator shortcut for the component that holds your Kinect data (currently named OUT in this example), you open it’s parameters and go to the Common page. Here you’ll find a parameter named Global OP Shortcut. Whatever you enter into this field will become the operator shortcut. If you enter Kinect into this field, there are some nifty things you can do now. You can get a dynamic operator link to that component simply by typing:
Too simple?? It really is. op.Kinect can now replace you needing to do something like:
That’s pretty amazing if you ask me. Now back to our original problem, you need to access some data inside of that component, an operator named KINECT_DATA. To this you could use:
If you were to translate this line into plain english, it would read: go to Operator shortcut named Kinect and inside of it, find another operator named KINECT_DATA. The best part about this link is that it’s dynamic. It doesn’t matter where you move the component to, your paths will not need updating or fiddling, everything will keep working as is. If you move the OUT container or the script calling it, neither would break. Both would continue working just fine. This can be extremely useful for fetching data from all around your project and will make nesting features and keeping your projects clean much easier.
We use Python scripts a lot, but sometimes you just need a tiny moment before an action happens. There are two methods of introducing delays in Python scripts. The first is the one most developers who’ve been using Python know. That is by getting the Operator object in Python using op() and then using the .run() method on it, and giving it an argument for delayFrames. If we were trying to delay the execution of a script named script2 by 30 frames it would look something like this:
op('text_dat_name').run(delayFrames = 30)
This would run the next script in 30 frames. At 60 fps, this would run 0.5 seconds later and at 30 fps it would run 1 second later. This works just fine, but what if you need to delay a bunch of quick single line commands, things like clicking on buttons or setting slider values or anything like that. It becomes a huge pain to start making new Text DATs with single lines in them like:
That doesn’t need a whole Text DAT to itself! This is where inline delays come in hand using the runs class and the run class (I know… they’re very similarly named…). The runs class holds a bunch of run objects, but that’s besides the point. How do you use it? Easy.
Let’s say you have a button named button1 and you want to click it 1 second after a print statement and you have slider named slider1 and you want to set it’s value to 0.5 another second after the button gets clicked. Instead of making a new Text DAT for each of these actions you can do something like this:
run("op('button1').click()", delayFrames = 1 * me.time.rate)
run("op('slider1').interactMouse(0.5, 0.5, left=True)", delayFrames = 2 * me.time.rate)
Just like that you can add the delays to the scripts right inline next to each other without needing to make new Text DATs. The way this works is that you give it a Python command as a string (that means inside a pair of quotation marks), and then you can give it a delayFrames just like you would when using the other .run() method. You’ll notice in this example I also used a trick where instead of specifying the amount of frames to wait, I calculate it by multiplying the time in seconds I want to wait by me.time.rate. This means that if I wanted something to wait for 1 second, it wouldn’t matter the frame rate, it would always calculate the correct amount of frames to wait when it is executing. One thing to be aware of with this method is you use of double and single quotations marks. I usually wrap my whole statement in double quotation marks (“) because I prefer to write my Python statements with single quotation marks (‘) to denote strings.
You could take it a step farther and create a variable that holds your Python command in a string, such as:
script = "op('button1').click()"
run(script, delayFrames = 1 * me.time.rate)
This is an overly simple example, but it shows you that you can compose the Python expression as a string separately and then just feed the variable to the run command. Since it’s just a string you can also do things like inject variables using string concatenation amongst many other things.
These two tricks probably sound super easy. They are! That’s what makes them great! I use both of these techniques almost every day, and Global OP Shortcuts are at the underpinning of my project architecture methodology. These simple things will carry for the rest of your TouchDesigner career and you should get used to using them as early as possible. So hopefully this post was an easy read that will provide a lot of value for you in both short and long term. Happy coding! And if you’re interested in more content like this, check out https://www.elburz.io/