Category Archives: TouchDesigner

TouchDesigner | Previs for Moving Lights

I got an interesting question a few weeks ago about how to use tracking data to control moving lights. If you work with Touch long enough, at some point you’ll almost always end up wanting to drive some object in the real world with information derived from calculations in Touch. Where / how can you get started with that process?! It’s often temping to straight away jump into just driving the physical object. After all you know what you’re trying to do, and you might have a sense of how to get there – so what’s the harm?

That’s not a bad instinct, but I almost always like to start with some form of previs. You won’t always have access to all the equipment you want to use, and more importantly it’s often better to make sure you understand the problem you’re trying to solve before you start driving motors. The additional bonus here is that solid previs will create some opportunities for testing, and planning that you might not other wise have.

This post is going to look at:

  • Planning / mapping out some simple previs for using tracking data on some moving lights
  • Using the Object CHOP to calculate bearings for rotation information
  • Some simple use of custom parameters and bindings
  • Pattern matching for renaming
  • Using the CHOP export method Channel Name is Path:Parameter
Top level look at our setup

As a disclaimer, this is not a great approach for LOTS of lights – but is a great way to get started and make sure you understand what you’re trying to accomplish.

Workspace Setup

I like a workspace where it’s easy to see multiple perspectives at the same time. In this case I’d like to see the the network editor (your typical Touch workspace), the geometry viewer, and a rendered view. We can split our workspace with the icon that looks like a little down arrow in the upper right corner of the pane bar:

Split the network with the drop down menu in the upper right

In this case I’m going to split the workspace left/right, and then once top/bottom. In the pane on the top right I’m going to change the Pane Type to geometry.

Change the pane type to geometry

On the Bottom I’m going to change the pane type to Panel. If we’re working with container COMPs this can be very handy as it lets us see the panel for the container. If you’ve created an interface, that means you can also interact with your controls from here, without having to open a floating window.

A clean project set up like this looks like:

Our blank network

Our last step here is going to be adding a Container COMP to our network. We also need to rename it, and make sure our two new windows on the right correctly reference our net container. Our network bar is path based, so we just need to make sure both of them have the address: /project1/container_previs

Container COMP with our correct addresses

Custom Parameters

I want to control the elements of my previs container with a set of custom parameters. This will help me reduce the places I have to look for making changes, and helps save me the step of building a UI to control this visualization. I already happen to know what pieces I want to add here:

  • Transform controls for a tracked object
  • Transform Controls for 3 lights
  • Color Controls for 3 lights
  • Dimmer Controls for 3 lights
  • All of these should live on a page called “Settings”

In the end, our custom parameters should look like this:

Our Custom Parameters

I’m not going to go into huge detail here about how to set up the custom parameters here, but you can learn more about using custom parameters:

There is, however, one quick thing to point out. The addition of bindings in the Spring update comes along with a handy way to take quick advantage of them. We can use the drag and drop trick to add a set of custom parameters from another operator, and we can also auto assign all of our bindings in the process. Let’s take a look at that process.

Inside of /project1/conatiner_previs I’m going to add a light COMP. I’m going to first open the custom parameters dialog, and then add a page to my previs container called Settings. Next I’m going to grab the parameter Translate from my light comp, and drag it right onto the Parameter column in the customize dialogue. Here’s where the magic happens. Next I’ll select Bind New Par as Master from the drop-down, and ta-da – now your bindings are already set up for you:

Auto-assign our bindings by dragging and dropping

Scene Set-up

To set up this scene I’m going to use a few simple tricks. First I’m going to use a camera and a few pieces of geometry to get started. For something like the stage, I like a single top level Geo, with separate pieces nested inside. The benefit here is that if we scale or transform our top level Geo those changes will propagate to our nested elements:

Inside out stage

You’ll notice a separate wire-frame version of the stage inside of our stage – this is to give us some nice grid lines. geo2 is also transformed ever so slightly above geo as well, so we’ve got some nice clean rendering. Looking at the phong material for the primary stage, it’s got a slight gray emit color – so we can see it even when there’s no light on it’s surface.

We’ll also use a little trick with our camera. I’ve set my camera to look at a null COMP in our scene. This gives us some better handles for adjusting where our camera is looking without needing to manually set the rotation and transformation values. This is often a very helpful and easy way to get better camera controls by thinking spatially, rather than as transformation values.

Finally, I’m going to add another geo and change it to be a sphere. In this case I want some object to represent a moving object on my stage. I’m going to bind this object to my parent’s custom parameters that I already set-up for transformation. This means I can change the position of this geo either from the parent’s custom ops, or from the parameters on the geo.

Light Set-up

Depending on the order you’ve done this, you may have already created lights to set-up your custom parameters. If you haven’t done that yet, now’s a good time to add some lights to your scene. I’m going to use three for now. I’m also going to use a table to hold the transformation information for our lights. I’m using a table here because I’m thinking of a situation where my lights aren’t going to move – theatrical lights are usually transformationaly stable, and instead just rotate. If you’re lights are going to move in xyz position, this isn’t the most optimal set-up. Next I’m going to convert my table of positions to CHOP data. In my Dat to CHOP I want to make sure that I’m using a channel per column, and that my fist row and first column are marked as names.

Convert from DAT to CHOP

Next I’m going to use an object CHOP to find the position data for my target (that sphere we set-up in our scene). I’m going to plug my datto1 into that a second object CHOP, and my first object CHOP into the second input. Next I’ll make sure that I’m computing bearing, and on the channel page I want to change the output range to be start/end. I want to change that to samples instead of seconds, and then make sure that I start at sample 0 and end at sample 2.

Object CHOP -bearing calculations

So what’s this all about? What we’re doing with this second object CHOP is calculating the rotational values that will tell us the how to look at our target with each of our lights. We can then use this to set the rotation of our lights so they follow our target object. We could also do this with a chain of Math CHOPs… but having done it both ways, they’re almost computationally identical, and this you can do with fewer operators. So now we know the rotation values we need to set our on lights to make sure they’re looking at our target.

Now, we could certainly write some complex references for these values, but we can also learn a handy trick that I don’t see used too many places. Here we’re going to look at another CHOP export configuration. Before we get there, we need to flatten out our CHOP data. To do this we can use a shuffle CHOP set to split all samples:

Flatten out our CHOP data

This is swell, but if we don’t want to go through the process of exporting these one by one to our lights, what can we do? Well, it’s handy to know that there’s another way to use CHOP exports. There happens to be an export method called Channel Name is Path:Parameter. What that means is that if we change the name of our channel to be formatted so it’s the path to the operator followed by a colon and ending with the target parameter the exports will happen for us without any extra work.

Let’s take a quick detour to see how that works in isolation first. Let’s first add a constant CHOP, connected to a null CHOP. Finally let’s add another geo COMP to see how this works.

First steps to understanding another export method

Next let’s name some channels in our constant. I want to add the following:

  • geo1:tx
  • geo1:ty
  • geo1:tz

Next on my null CHOP I’m going to go the common page and change the export method to Channel Name is Path:Parameter. Finally, I’m going to turn on the export flag for the null CHOP, and ta-da. You’ve not exported values for tx, ty, and tz to your geo.

Exporting CHOPs with Path and Parameter

Okay, now if we go back to our flattened rotation info, we can imagine that if we just change the names of our channels we won’t have to do lots of dragging and dropping to get our exports sorted.

Let’s add a rename CHOP, and we can use some fancy pattern matching to do the renaming for us. In the From parameter we want r*[0-2]. What on earth does that mean? Well, any channel that starts with r, then has any character next, and then has a value of 0, 1, then 2. This happens sequentially, so we do all the r0s first, then the r1s and so on. That matters because our shuffled data is all rx values, then ry, and finally rz. We have to make a pattern matching schema that works works with that pattern.

Okay, so in our To parameter we want to use the pattern light[1-3]:r[xyz]. This means we’ll change our name space to be something like light1:rx. Again, this happens sequentially, so we’ll do all the 1s, then 2s, then 3s. What we end up with changes our original names like this:

Next we should be able to connect our null CHOP, set it to export as Channel Name is Path:Parameter, and we should be off to the races with all of our exports set up.

Our Container

By the end of all of this we should have a handy little container that’s set-up so we can change the position of a target geometry, and have our lights automatically follow it around our stage. If you’ve gotten stuck along the way, check the bottom of the page for a link to a repo where you can download just a tox of the finished Container, or a whole toe file with our workspace setup.

Our little follow spots

Other Considerations

What we haven’t talked about is getting your measurements and scaling right, or how to convert our rotational values into pan and tilt measurements, or how to convert that for controlling something with a protocol like DMX. Those are big concerns in their own right, but with a solid visualization you will at least have something to compare the real world against so you can start pulling apart those challenges.

Happy programming!


If you want to download this and look through the set-up you can find it on GitHub here.

TouchDesigner | Virtual MIDI Devices

In the past week I’ve found myself in multiple conversations about working with MIDI in TouchDesigner. Specifically, how you might pass a MIDI signal from one application to another on the same computer. So far, all of the conversations I’ve been involved in have been related to working with Windows – which means that this particular post is focused on that operating system, though the same idea should transfer to macOS if that’s your current platform.

So what’s the problem exactly?

All of my conversations about this topic have been centered around sending messages from TouchDesigner to WATCHOUT. For those unfamiliar, WATCHOUT is a media server application that’s used in Theatrical and live events contexts for controlling or sequencing media playback. It’s in the same family of tools as Disguise, GreenHippo, and Pandora’s Box. You may well be asking – “why not just use OSC?” That’s a great question, some versions of WATCHOUT don’t support OSC, though they do support MIDI. While this is the specific challenge, the more general idea that we can push against here is:

How can I locally (on the same machine) test a MIDI signal flow without adding extra hardware?

To get there we need to add in a virtual device to allow us to loopback. At it’s simplest, a loopback is just a way of capturing a signal that you’d other wise be sending somewhere else. Imagine using a cable from the headphone port on your laptop, and connecting it to the microphone in on your laptop (don’t actually do that, just imagine it). Why? Well, using actual interfaces often have different requirements, or set-up than just faking it – you’ll also likely find that operating systems don’t have an implicit knowledge of a particular signal or transfer format. Just because your computer is making noises doesn’t automatically mean that it can capture that noise locally.

How do we make this work then?

Enter the Virtual Device. A Virtual Device provides drivers that act as though it’s sending your signal to a dedicated piece of hardware, but instead allows you to rout it back to your machine. There are lots of examples of this in the audio world, and it just so happens that there’s a Virtual MIDI device we can install to pass around MIDI signals as well.

Doing a bit of googling I found LoobBe1. It has a free evaluation license, which is what we’ll use below. If you decide that this needs to be part of your commercial workflow, I’d encourage you purchase a license. It’s less than $15 US, and supporting folks that make useful tools is something we should all do more. As a disclaimer, I don’t have any affiliation or connection to LoopBe1 – I just found them after doing some internet searching.

We’re going to set-up a simple loopback test for a MIDI signal from TouchDesigner, and see if we can read that signal back. I don’t have a copy of WATCHOUT to work with, but if we can trouble shoot within TouchDesigner, we should at least know that it’s working.

Getting a MIDI Loopback Working

Ingredients

Windows 10 v1809
TouchDesigner 099 2019.15230
LoobBe1

To get started you’ll need to download and install LoobBe1. I like to limit what starts up automatically on my computer, so I’d recommend also turning off the automatic start-up of this application once you have it installed.

From there we can start up TouchDesigner and begin our configuration process. We’re going to start by opening the MIDI Mapper dialogue. You can open that with alt + d or you can find it from the Dialogues drop down:

Open the MIDI Mapper dialogue from the Dialogs drop-down

You should now see the MIDI Mapper:

The MIDI Mapper Dialog window

We’re going to test our output right in TouchDesigner. To do that let’s click on the Create New Mapping button in the middle left of the dialogue window. That will add a row to our Device Mapping where we can specify that both our In and Out Device will be LoopBe Internal MIDI.

The MIDI Mapper with an added Device

Before we can move forward here it’s important to recognize that the naming of MIDI channels matters. MIDI (Music Instrument Digital Interface) has a detailed specification that ensures continuity across devices and manufactures, so it shouldn’t be a surprise that there’s lots of detail here. You can read the whole spec by here, or get a summary from Wikipedia. What we really care about today is that we need to format our TouchDesigner channel names in a way that’s compatible with Derivative’s implementation. To figure all of that out, we need to head over to the wiki for a quick read. Let’s take a look at the MIDI Out CHOP page. Here we see that:

Naming the CHOP channels: Channels are mapped to events by their name. Events like notes, controllers and velocities must be followed by the note/controller number (n65, c7). If the number is left off a note event, the note number is the value of the channel. Other events, which are sent to the entire channel, do not need a trailing number (pc, pw). The channel prefix can be used to identify the MIDI channel the event should be sent on (i.e. “ch1n45” assigns that TouchDesigner channel to note 45 messages on MIDI channel 1). Channels can always be renamed with a Rename CHOP before entering the MIDI Out CHOP.

The MIDI Out CHOP sends MIDI velocity as well. The values of the channels entering the MIDI Out CHOPs are sent as the velocity of the note. If Normalize is “None”, the channel needs to be 0 to 127. If Normalize is “0 to 1”, channel values between 0 and 1 are scaled to be MIDI 0 to 127.

The “Cook Every Frame” option cooks the CHOP every frame, even if the CHOP isn’t being displayed. All Volume Off and All Volume On flags are new and emit events for Controller 7 of all 16 channels. MIDI output go in a separate thread to allow output that slows TouchDesigner less. It now works in Time Slice mode for note events and controller events. (Not for Program Change or Sysex messages yet) Note channels only trigger anew Note On when the input channel goes from 0 or less to a value greater than zero. Similar for Note Off events.The channel name determines how it is interpreted.

Derivative’s MIDI Out CHOP Wiki Page

What does that even mean?! Well, let’s set up a few pieces in a network to see if we can make sense of this.

I’m going to add a Noise CHOP to my network, and turn on the time-slice parameter so we have some constantly changing values. I’m going to change the Channel name parameter to be ch1c1. This would be Channel 1, Control 1.

By default our Noise CHOP will have values between -1 and 1. Based on what’s in the wiki, we’ll either need to normalize our values (change them to be in a range of 0-1) or we’ll need to change our values to be in a range of 0-127. We can do either of these with a Math CHOP so let’s add one of those in line and change our range parameters:

Now we can finish this off by adding a MIDI Out CHOP to our network:

So how do we make sure this is working?

Well, let’s first check with Touch. Since we added our LoopBe Internal MIDI as our input device, we should now be able to drop in a MIDI In CHOP to see our midi values:

Great – so we can see our values… only they’re expressed as whole numbers rather than as a floating point value. Why? MIDI works with 127 steps, so our MIDI Out CHOP is converting our 0-1 set of values into a range of 0-127. What this means is that you, dear developer-artist, have to make a choice. This approach works perfectly fine, but it may mean that at some point you’ll be doing some mental gymnastics in understanding how values in Touch correspond to values in your other application. If that’s too much to think about all the time, then you may want to change the math CHOP to be in a range of 0-127, with the values rounded either up or down (depending on your preference). This kind of change would let you see exactly what value you’re expected to be transmitted from Touch. If you’re working on a high profile AV installation, this kind of specificity might be worth having – if only so you can say with complete confidence that you’re transmitting a value of x on channelxyz.

Okay, so it works in Touch… but can we be sure? Of course. I’m going to use Isadora by Troikatronix to help us validate that we’re sending this correctly. I’m not going to go into too many of the details for how we set-this up in Izzy, just enough so we can validate what we’re seeing here.

To start I’m going to open up Isadora, go to the Communications drop down, and select MIDI Setup:

Like Touch, I’m going to set the Input Port to be LoopBe Internal MIDI:

To see if we’re actually transmitting values, let’s open up the Izzy Status monitor:

Sure enough, we should see a blinking Green light on Channel 1 and the value associated with it:

TADA! You’re passing data around locally from one application to another using MIDI.

Takeaways

Okay, so what exactly did we do? Well, we added a virtual MIDI interface that let us send MIDI encoded data between applications on a single computer. We then verified that this works by first looking at the data transmission in Touch, then further validated that this works working by looking outside of Touch with another application.

Why on earth do any of this? Well, that’s really a question for you. If MIDI is a core part of your work flow, and passing data between applications on the same computer has an important role for you, then the answers might seem obvious. For those still scratching their heads, imagine that you have raw sensor data that’s not MIDI friendly, but you’d like to get it into an application that does speak MIDI. This kind of workflow is great for that. Working with a Kinect but want to turn that data into MIDI, easy. What about a Leap – sure. Can I do my mouse – you betcha.

It might not be what you need today, but maybe it’ll inspire you to think about some mischief you could get yourself into.

Happy Programming.

Guest Post with Elburz Sorkhabi | Two TouchDesigner Beginner Tricks

If you’re reading this, you’re probably a fan of Elburz Sorkhabi and his work. That makes two of us! Elburz and I are always trying to find ways to collaborate and get into trouble, but we’re often on other sides of the world from one another. We thought it would be fun to do some guest blog posts for each other’s readers. So today you’re in for a treat with a guest post from Elburz himself! If you like a little variety in life, give Elburz blog a read over at Elburz.io.


TouchDesigner is a feature-rich software. It can be daunting for new users what they should be learning and what they’re missing in their toolbox. I thought it would be nice to share two TouchDesigner tricks that are easy to learn and will provide you a lot of value over your career. Some of these things can even evade experienced users as they come quickly in new updates and over time it can be hard to keep track of them all. With that said, let’s dive in!

Custom parameters

Custom parameters are one of the best features to come to TouchDesigner in the last few years. I use them all the time. Sometimes I use them to wrap complex functionality inside of a component while providing easy to use controls. Otherwise I use them for more architectural elements of a project, such as creating internal APIs. The great thing is that any kind of parameter type already available in TouchDesigner can be used for your own custom components. One thing to note is that you can only add Custom Parameters to COMP operators. The first step is to make a COMP, which usually will be a Container COMP or Base COMP, and then right click on it and select Customize Component…

This will open the Component Editor. In this window you can do things like make new extensions, and more importantly for us, this is a visual way to create Custom Parameters. The next step is type a name in to the top-left string field that will be used to name the new parameter page. Then go ahead and click Add Page. I use names like Settings or Controls for my parameter pages. You can confirm everything worked by checking the parameters of your COMP and looking for your new parameter page. It’ll be blank for now.

The next step is to start adding parameters to our new parameter page. Click on the parameter page you just created on the left side of the Component Editor. Now you can enter a name for the parameter you’re about to create in the second string field. Then we’ll go ahead and use the drop down menu to choose the type of parameter you’d like to create. Like I mentioned earlier, you can create any of the existing types of parameters including pulse buttons, toggles, colour pickers, file/folder selectors, and more. The drop down menu to the right of the parameter type has numbers from 1 to 4. These numbers represent the amount of value parts for parameters such as floats and integers. Parts can be thought of as the “amount” of values, for example if you make an integer with 3 selected, you’ll get an integer parameter with 3 separate integers, similar to an RGB parameter field. For this example, let’s select Toggle, name it Toggle Button, and click Add Par. You’ll immediately see the parameter appear in your parameter window as well as the component editor.

That’s it! That’s all there is to making custom parameters. The process is the same for any type of parameter, you just have to choose what you want from the drop down menu. How to use the values is our final step here, but it’s also quite easy.
The quickest way to access the parameter values of a COMP are to use the Parameter CHOP. When you drop one inside of the COMP, by default it’ll already be setup to show you only the values of your custom parameters. For most parameter types, it isn’t any more complicated than using these CHOP channels as you would any other channels for referencing.

For the parameters that hold non-numeric data, such as the file/folder selectors or string fields, you’ll need to access the data through some simple Python scripting. In this case, we can place a Parameter DAT right next to our Parameter CHOP. In the OP parameter enter .. which will select the parent container, for the Parameters enter *, and finally turn off the Built-In button. The Parameter DAT has callbacks like many of the other Execute type of DATs in TouchDesigner. This means that it has different functions where you add your code based on triggers. To access our same toggle button via the Parameter DAT we could add this under the onValueChange() callback:

if par.name == "Togglebutton":
    print(par.val)

This would parse the different customer parameters by name (par.name), find the toggle parameter by it’s scripting name (Togglename – the name you see when you click on a parameter and see it’s second name in the expanded area), and then prints it’s value (val).

Operator snippets

I’m still surprised to this day by how Operator Snippets isn’t talked about every day by new users. It’s the most helpful resource added that can help you learn how to use just about any operator. Operator Snippets is a project file that is built into TouchDesigner that contains tons and tons of examples of how to use operators. It’s similar to Max MSP’s examples per node.
There are two ways to access them. The first is to click Help in the top menu and then select Operator Snippets. This will open up a new window. The left side of the screen looks similar to the OP Create Dialogue (the menu when you double click on the network background) and allows you to choose which operator you want to find examples for. Underneath the operator selection area are a handful of big buttons with different names. Click on one of these changes between the different examples for the operator you’ve selected. On the right hand side of the window is the network area with the example in action.
The great thing about Operator Snippets is that the example is a live network running in real-time! You can copy the example, paste it into your project, and then tweak it to your needs. How useful it that?

Wrap up

These two tricks may sound simple but they are game changing. Being able to make your own custom parameters quickly and easily is game changing. You can make complex components that can be easily used by anyone or implemented into your own projects and controlled easily. Operator Snippets give you the greatest documentation you could ever ask for: live networks running in real-time. I bet if you spent a little bit of time just browsing the snippets, you’d find awesome little examples you’ll be eager to copy and paste into your next project. With that said, enjoy these two beginner tricks and happy programming! And if you’re interested in more content like this, check out elburz.io

TouchDesigner | New Features | Bindings

Spring time is lots of things – flowers, holidays, vigorous allergies, and the TouchDesigner Spring Update. For the second year running this is the time of year that features graduate from just being in experimental to being full fledged stable release features. Wowza.

This spring we’re seeing a feature that’s flat out amazing, and likely a bit of a sleeper. Bindings. Elburz has a great shout out to bindings on a recent blog, and I wanted to take some time to dig in and step through an example of both why they’re important, the paradigm they’re built on, and why they matter.

So what are parameter bindings anyway? The Derivative wiki has a great segment describing bindings:

Bound parameters keep their values in sync and will respond to changes from either parameter. For each parameter that is a bind master, it will have one or more bind reference parameters.
The bind master holds the current value. It can have exports and expressions and generally works like any other parameter, but its value can be also changed indirectly by the bind references.
A bind reference holds the location of the bind master. A bind reference is in a fourth “Bind” Parameter Mode, and will show up as purple text in parameter dialogs. It can only be changed via its its bind master, its UI, and its val property.

Derivative Wiki article on Binding

Model View Controller – MVC

Errm. Okay, so what does that mean exactly? Bindings are based on an interface architecture paradigm called Model – View – Controller, or MVC. Wikipedia has a nice starter debrief on the idea for us, but it’s easy to understand if you’ve spent much time working in Touch either for live set, or for a client application. Let’s consider a live set to help us get our footing here. Suppose you have parameter that can be updated by multiple touch points both in your own UI and in the TD UI. We all know this game, as soon as you export a CHOP to a parameter, you can no longer change that parameter except through the exported CHOP.

Fine.

So maybe instead of exporting from a single CHOP you instead write a bunch of scripts to handle this operation – only now you’re in a real pickle. Why? Well, because your script changes the parameter, but doesn’t update all of the UI elements that reflect the state of that parameter. So you write another script to update the UI. But now you’ve managed to save your project in a state where the UI and the parameters are not aligned. As soon as you change the UI everything is in sync – so you save over a few things, commit your changes, and now everything should be great. Until you need to load a saved preset state from disk. Now you’ve gotta write another set of scripts to do all of that updating, or hunker down for a more generalized solution – which probably means a code refactor. Who wanted to make some more sweet visuals anyway? There goes your night off. There goes your margin. Sigh.

The real world example of this is light switches. If you’ve ever lived in an apartment where multiple light switches control the same light / outlet, you understand this issue intimately. How do you know if the light is on or off? Only by looking at the light, because once the states of the light switches are out of phase they perform the opposite action.

The Model – View – Controller paradigm is a design architecture that’s intended to help resolve this issue. The MVC approach decouples the control of a UI element from the data it is manipulating. You could do this in touch before, you were just on the hook for doing all the set-up. This probably meant you had a master table or storage dictionary somewhere that was updated whenever a parameter was changed, that in turn would update all the other touch points. That’s a huge hassle, but it was the only way to solve this problem. It’s also the kind of silly thing you could really have a strangely strong opinion about – and consequently be convinced that your collaborators were doing it all wrong.

Enter Bindings

Okay, so as a refresher, Bindings are a new parameter mode – that’s the little multi-colored set of dots next to any parameter. This new mode is purple – one of the many colors of awesome. At the end of all of this, we’ll take a peek at the new widgets – and the UI redesign they offer, but to get started let’s build a use case for bindings so we can get a sense of what they’re good for.

Slider

We’re going to start with a good old fashioned slider. Why a slider?! Well, this is the kind of parameter we end up using all the time, and the fundamental nature of this UI piece should be foundational enough that if we can get a handle on this one, the jump to more abstract ideas should be a little easier.

Let’s get started by first adding a slider from the Op Create dialogue. We’re just going to add a run of the mill slider for now.

adding a slider

From here we’re going to customize our slider – let’s add a page called “Settings” and then a float custom parameter that we call “Slider” for now.

customizing our slider

So far so good. The next step is where it’s gonna get a little weird, and where it’ll get different than before. From here, let’s dive into our slider. We’re going to delete our Panel CHOP, and add our own Constant CHOP.

changing slider internals

Okay. Now here’s the wild part. On our new Constant CHOP we’ll use the new bindings parameter mode to write parent().par.Slider – that’s the reference to our newly created custom parameter.

write our binding expressing directly

If you’re not into that whole writing expressions exercise, you can also do this with a little drag-and-drop action:

drag and drop binding

Okay… so why is this interesting. Well, let’s see what happens when we move our new custom parameter, or move our Constant CHOP:

binding in action

Slick. Okay. That’s fly. So if we update the our parameter or our CHOP both changes are reflected in the other operator. Now let’s make a few final changes so that when we interact with the sliders panel we update both of these values. We can do this with a Panel Execute DAT. Let’s add our DAT and modify the contents with the following:

def onValueChange(panelValue):
     parent().par.Slider = panelValue
     return
adding our panel execute DAT

We should now see that if we move the slider that our Slider parameter updates, and our Constant CHOP updates.

panel and parameter binding

We’re very close now. All we need to do is to update the knob component in our slider. We need to change the expression there to be:

parent().par.Slider*parent().width-me.par.panelw/2
updated panel script

There we have it. Now we can change our custom parameter, our Constant CHOP, or the slider and all three stay in sync. MAGIC.

binding all around

Widgets?

Early on I mentioned that we find this same behavior in Widgets. What exactly are widgets you ask – currently Widgets are rolling out as a huge overhaul to the TUIK interface building kit that was relatively ubiquitous in TouchDesigner networks. Widgets are more modern take on UI building, and offer significant advances to UI building approaches for Touch. There’s too much to cover about widgets here, but it’s worth pointing out that the same binding approach you see above is a fundamental element of the widget system. It allows for bidirectional control of both user interaction elements and parameters – the core principle we just explored. You can dig in and learn a little more about widgets by reading through the article on the Derivative Wiki.

Why Bind…

You might be looking at this and feeling like it’s outside of your wheelhouse, or your workflow.

A reasonable reflection.

Regardless, I’d encourage you to think about the times when you’ve wanted to both control from more than one location – the parameter itself, as well as some other control interface. If nothing else, give them a try to see where they might fit – if they’re no good for you, don’t use them… though I suspect you’ll find they have all sorts of exciting uses.

Happy Programming!

TouchDesigner | Case Study | Custom Parameters and Cues

I recently had the good fortune of being able to collaborate with my partner, Zoe Sandoval, on their MFA thesis project at UC Santa Cruz – { 𝚛𝚎𝚖𝚗𝚊𝚗𝚝𝚜 } 𝚘𝚏 𝚊 { 𝚛𝚒𝚝𝚞𝚊𝚕 } Thesis work is strange, and even the best of us who have managed countless projects will struggle to find balance when our own work is on the line – there is always the temptation to add more, do more, extend the piece a little further, or add another facet for the curious investigator. Zoe had an enormous lift in front of them, and I wanted to help streamline some of the pieces that already had functioning approaches, but would have benefited from some additional attention and optimization. Specifically, how cues and states operated was an especially important area of focus. I worked closely with the artist to capture their needs around building cues / states and translate that into a straightforward approach that had room to grow as we made discoveries, and needed to iterate during the last weeks leading up to opening.

The Big Picture

{ remnants } of a { ritual } is an immersive installation comprised of projection, lighting, sound, and tangible media. Built largely with TouchDesigner, the installation required a coordinated approach for holistically transforming the space with discrete looks. The projection system included four channels of video (two walls, and a blended floor image); lighting involved one overhead DMX controlled instrument (driven by an ENTEC USB Pro), and four IoT Phillips Hue lights (driven by network commands – you can find a reusable approach on github); sound was comprised of two channels driven by another machine running QLab, which required network commands sent as OSC. The states of each of these end points, the duration of the transition, and the duration of the cue were all elements that needed to both be recorded, and recalled to create a seamless environmental experience.

Below we’re going to step though some of the larger considerations that led to the solution that was finally used for this installation, before we get there though it’s helpful to have a larger picture of what we actually needed to make. Here’s a quick run-down of some of the big ideas:

  • a way to convert a set of parameters to python dictionary – using custom parameters rather than building a custom UI is a fast way to create a standardized set of controls without the complexity of lots of UI building in Touch.
  • a reusable way to use storage in TouchDesigner to have a local copy of the parameters for fast reference – typically in these situations we want fast access to our stored data, and that largely looks like python storage; more than just dumping pieces into storage, we want to make sure that we’re thoughtfully managing a data structure that has a considered and generalized approach.
  • a way to write those collections of parameters to file – JSON in this case. This ensures that our preset data doesn’t live in our toe file and is more easily transportable or editable without having TouchDesigner open. Saving cues to file means that we don’t have to save the project when we make changes, and it also means that we have a portable version of our states / cues. This has lots of valuable applications, and is generally something you end up wanting in lots of situations.
  • a way to read those JSON files and put their values back into storage – it’s one thing to write these values to file, but it’s equally important to have a way to get the contents of our file back into storage.
  • a way to set the parameters on a COMP with the data structure we’ve been moving around – it’s great that we’ve captured all of these parameters, but what do we do with this data once we’ve captured it? The need here is thinking through what to do with that data once you have it captured.

Cuing needs

One of the most challenging, and most important steps in the process of considering a cuing system is to identify the granularity and scope of your intended control. To this end, I worked closely with the artist to both understand their design intentions, as well their needed degrees of control. For example, the composition of the projection meant that the blended floor projection was treated as a single input image source; similarly, the walls were a single image that spanned multiple projectors. In these cases, rather than thinking of managing all four projectors it was sufficient to only think in terms of the whole compositions that were being pulled. In addition to the images, it was important to the artist to be able to control the opacity of the images (in the case of creating a fade-in / out) as well as some image adjustments (black level, brightness, contrast, HSV Offset). Lighting, and sound had their own sets of controls – and we also needed to capture a name for the cue that was easily identifiable.

As lovely as it would be to suggest that we knew all of these control handles ahead of time, the truth is that we discovered which controls were necessary through a series of iterative passes – each time adding or removing controls that were either necessary or too granular. Another temptation in these instances is to imagine that you’ll be able to figure out your cuing / control needs on your feet – while that may be the case in some situations, it’s tremendously valuable to instead do a bit of planning about what you’ll want to control or adjust. You certainly can’t predict everything, and it’s a fool’s errand to imagine that you’re going to use a waterfall model for these kinds of projects. A more reasonable approach is to make a plan, test your ideas, make adjustments, test, rinse, repeat. An agile approach emphasizes smaller incremental changes that accumulate over time – this requires a little more patience, and a willingness to refactor more frequently, but has huge advantages when wrestling with complex ideas.

Custom Pars

In the past I would have set myself to the task of handling all of these controls in custom built UI elements – if was was creating an interface for a client and had sufficient time to address all of the UI / UX elements I might have taken that approach here, but since there was considerable time pressure it was instead easier (and faster) to think about working with custom parameters. Built in operators have their own set of parameters, and Touch now allows users to customize Component operators with all of the same parameters you find on other ops. This customization technique can be used to build control handles that might otherwise not need complete UI elements, and can be further extended by using the Parameter COMP – effectively creating a UI element out of the work you’ve already done while specifying the custom parameters. The other worthwhile consideration to call out here is your ability to essentially copy parameters from other objects. Consider the black level, contrast, and brightness pars included above. One approach would be to create each par individually, and set their min, max, and default values. It would, however, be much faster if we could just copy the attributes from the existing level TOP. Luckily we can do just that with a small trick.

We start by creating a base Comp (or any Component object), right clicking on the operator, and selecting customize component.

This opens the customize component dialogue where we can make alterations to our COMP. Start by adding a new page to your COMP and notice how this now shows up on the components parameters pages:

For now let’s also add a level TOP so we can see how this works. From your level TOP click and drag a parameter over to the customize component dialogue – dragging specifically to the Parameter column on the customize component dialogue:

This process faithfully captures the source parameter’s attributes – type, min, max, and default vals without you needing to input them manually. In this style of workflow the approach is to first start by building your operator networks so you know what ops you will want to control. Rather than designing the UI and later adding operator chain, you instead start with the operator chain, and only expose the parameters you’ll need / want to control. In this process you may find that you need more or fewer control handles, and this style of working easily accommodates that kind of workflow.

Capturing Pars

Creating a component with all of your parameters is part of the battle, how to meaningfully extract those values is another kettle of fish. When possible it’s always handy to take advantage of the features of a programming language or environment. In this case, I wanted to do two things – first I wanted to be able to stash cues locally in the project for fast retrieval, second I wanted to have a way to write those cues to disk so they were’t embedded in a toe or tox file. I like JSON as a data format for these kinds of pieces, and the Python equivalent to JSON is dictionaries. Fast in TD access means storage. So here we have an outline for our big picture ideas – capture the custom parameters, stash them locally in storage, and write them to disk.

One approach here would be to artisanaly capture each parameter in my hipster data structure – and while we could do that, time fighting with these types of ideas has taught me that a more generalized approach will likely be more useful, even if it takes a little longer to get it right. So what does that look like exactly?

To get started, let’s create a simple set of custom parameters on a base COMP. I’m going to use the trick we learned above to move over a handful of parameters form a level TOP: Black Level, Contrast, and Opacity:

To create a dictionary out of these pars I could write something very specific to my approach that might look something like this snippet:

At first glance that may seem like an excellent solution, but as time goes on this approach will let us down in a number of ways. I wont’ bother to detail all of them, but it is worth capturing a few of the biggest offenders here. This approach is not easily expanded – if I want to add more pars, I have to add them directly to the method itself. For a handful, this might be fine, but over ten and it will start to get very messy. This approach also requires duplicate work – the key name for values means that I need to manually verify if the key name and parameter name match (we don’t have to do this, but we’ll see later how this saves us a good chunk of work), if I misspell a word here I’ll be very sorry for it later. The scope of this approach is very narrow – very. In this case the target operator variable is set inside of the method, meaning that this approach will only ever work for this operator, at this level in the network. All of that and more largely mean that while I can use this method very narrowly, I can use this approach, but I’m going to be sorry for it in the long run.

Instead of the rather arduous process above, we might consider a more generalized approach to solving this problem. Lucky for us, we can use the pars() method to solve this problem. The pars() method is very handy for this kind of work, the major catch being that pars() will return all of the parameters on a given object. That’s all well and good, but what I really wanted here was to capture only custom parameters on a specific page, and to be able to ignore some parameters (I didn’t, for example, need / want to capture the “save cue” parameter). What might his kind of approach look like, let’s take a look at the snippet below.

Abstract Reusable code segment

What exactly is happening here? First off, this one is full of documentation so our future selves will know what’s happening – in fact this is probably more docstring than code. The big picture idea is rather than thinking about this problem one parameter at a time, we instead what to think of entire custom pages of parameters. Chances are we want to re-use this, so it’s been made to be fairly general – we pass in an operator, the name of the page we want to convert to a python dictionary, the name of our newly made preset, and a list of any parameters we might want to skip over. Once we pass all of those pieces into our function, what we get back is a dictionary full of those parameters.

Capture to Storage

Simply converting the page of parameters to a dictionary doesn’t do much for us – while it is a very neat trick, it’s really about what we do with these values once we have them in a data structure. In our case, I want to put them into storage. Why storage? We certainly could put these values into a table – though there are some gotchas there. Table values in TouchDesigner are always stored as strings – we might think of this as text. That matters because computers are notoriously picky about data, and find the challenge of differentiating between words, whole numbers, and numbers with decimal values very difficult. Programmers refer to words as strings, whole numbers as integers or ints, and numbers with decimal values as floats. Keeping all of our parameter values in a table DAT means they’re all converted to strings. Touch mostly does an excellent job of making this invisible to you, but when it goes wrong it tends to go wrong in ways that can be difficult to debug. Using storage places our values in a python dictionary where our data types are preserved – not converted to strings. If you’re only working with a handful of cues and a handful of parameters this probably doesn’t matter – but if you’re thinking about 20 or more parameters it doesn’t take many cues before working in native data types will make a big difference. For reference, an early iteration of the cuing system for this would have needed the equivalent of a table DAT with over 1000 rows to accommodate the stored parameters. These things add up quickly, more quickly than you first imagine that they might.

Okay, so what’s an example of a simple and reusable function we might use to get a dictionary into storage:

Write to file

Similar to the above, we likely want a simple way to write our stored cues to disk in the same format we’re using internally. Python dictionaries and JSON are nearly interchangeable data structures and for our needs we can think of them as being the same thing. We do need to import the JSON module to get this to work correctly, but otherwise this is a straightforward function to write.

What you end up with will look like this:

Reading from file

We’re close now to having a complete approach for working with cues / states. Our next puzzle piece here would be a way to read our JSON from disk, and replace what we have in storage with the file’s contents. This means that whatever is in the file can be used to replace what we have in storage.

What you end up with here might look like this:

Loading pars – does it work

This part is the most tricky. Here the the big idea is to create a duplicate operator that has all of the same custom parameters in our preset maker. Why? Well, that would mean that all of the parameter names match – so which would make loading parameters significantly easier and more straightforward. The other trick here is to remove any of the ignored pars from our ignore list – thinking back this is to ensure that we don’t use any of the parameters that we don’t want / need outside of recording them. We can start this process by making a copy of our operator that’s being used to capture our parameters and then deleting the pars we don’t need. Next we need to write a little script to handle moving around all of the values. That should look something like this:

Making a Module

All of this is a good bit of work, and if you’ve been following along, you probably now have a handful of text DATs doing all of this work. For the sake of keeping tidy, we can instead put all of this into a single DAT that we can use as a python module. Wrapping all of these pieces together will give us something like this:

If you want to see how this works and pull it apart from there you can pull an example TOE file form this repo.

TD JSON – another alternative

There’s another alternative to this approach – which is the new TD JSON elements that are in the TouchDesigner. You can read about them on Derivative’s wiki here. These tools are a promising alternative, and you can do very similar pieces here. In particular we might use something like pageToJSONDict()to do what we’re after. That might look something like this:

That’s slick, but what we get back is almost 75 lines worth of JSON. This feels a little overkill to me for what we’re after here – there’s lots of potential here, but it might be a little more than we need for this actual implementation. Maybe not though, depending on how you want to change this process, it might be just perfect.

Safety Rails

There are some pieces missing in the approach above that I ended up including in the actual implementation for the artist – I’m not going dig into some of these pieces, but it is worth calling attention to some of the other elements that were included. The biggest pieces that needed to be addressed were how we handle failure, duplicates, provided confirmation on an operation, or warned the user about possibly unintended operations.

The artist, for example, wanted to both have the UI flash and to get a message printed to the text port when a preset was successfully saved. The artist also wanted to make sure that a preset wasn’t automatically overwritten – instead they wanted to see a pop up message warning that a preset was going to be overwritten, allowing the user confirm or cancel that operation.

That may seem unnecessary for a tool you build for yourself… until it’s 2am and you haven’t slept, or you’re working fast, or there’s a crit in 10 minutes and you want to make one more adjustment, and and and, or or or. Handling these edge cases can not only add piece of mind, but also ensure you keep your project on the straight and narrow.

Additionally, how you handle failure in these situations is also important to plan – we never want these pieces to fail, but having a gracefully solution for how to handle these moments are tremendously important to both work through and plan. If nothing else, it’s elegantly handling the failure and printing a message – better still is if give yourself a clue about what went wrong. A few breadcrumbs can go a long way towards helping you find the trail you lost. In my opinion, failing code gets a bad wrap – it’s something we grumble over, not something we celebrate. The truth of the matter, however, is that failures are how we make projects better. Being able to identify where things went wrong is how you can improve on your process. It’s a little thing, but if you can shift (even if only slightly) how you feel about a failing process, it will allow you some room to embrace iterative process more easily.

Conclusions

Managing states / cues is tricky. It’s easy to consider this a rather trivial problem, and it isn’t until you really take time to think carefully about what you’re trying to achieve that you uncover the degree of complexity in the questions around how you manage the flow of information in your network. You wont get it right the first time, but chances are you didn’t ride a bike without a few falls, and you probably didn’t learn to play that instrument without getting a few scales wrong. It’s okay to get it wrong, it’s okay to refactor your code, it’s okay to go back to the drawing board as you search to find what’s right – that’s part of the process, it’s part of what will ultimately make for a better implementation.

No matter what, hang in there… keep practicing, leave yourself breadcrumbs – you’ll get there, even if it takes you longer than you want.

Happy programming.


Zoe Sandoval’s { remnants } of a { ritual }

You can see { remnants } of a { ritual } and the work of the DANM MFA Cohort through May 12th at UC Santa Cruz.

TouchDesigner | Save External

a simple save external tox and text helper
clone or download from github

TouchDesigner Version

  • 099 2018.26750

OS Support

  • Windows 10
  • macOS

Summary

Working with git and TouchDesigner isn’t always an easy process, but it’s often an essential part of the process of tracking your work and collaborating with others. It also encourages you to begin thinking about how to make your projects and components more modular, portable, and reuseable. Those aren’t always easy practices to embrace, but they make a big difference in the amount of time you invest in future projects. It’s often hard to plan for the gig in six months when you’re worried about the gig on Friday – and we all have those sprints or last minute changes.

It’s also worth remember that no framework will ever be perfect – all of these things change and evolve over time, and that’s the very idea behind externalizing pieces of your project’s code-base. An assembly of concise individually maintainable tools is often more maintainable than rube golbergian contraption – and while it’s certainly less cool, it does make it easier to make deadlines.

So, what does all this have to do with saving external tox files? TOX files are the modules of TouchDesigner – they’re component operators that can be saved as individual files and dropped into any network. These custom operators are made out of other operators. In 099 they can be set to be private if you have a pro license – keeping prying eyes away from your work (if you’re worried about that).

That makes these components excellent candidates for externalization, but it takes a little extra work to keep them saved and sycned. In a perfect world we would use the same saving mechanism that’s employed to save our TOE file to also save any external file, or better yet, to ask us if we want to externalize a file. That, in fact, is the aim of this TOX.

Supported File Types

  • .tox
  • .py
  • .glsl
  • .json

In addition to externalizing tox files, it’s often helpful to also externalize any files that can be dffed in git – that is any files you can compare meaningfully. When it comes to your version control tool, this means that you can track the changes you or a team member have made from one commit to another. Being able to see what changed over time can help you determine why one version works and another does not. Practically speaking, this usually comes in the form of python files, glsl, or json files. This little tool supports the above file types, and goes a little further.

“What’s further mean?” You ask – and I’m so glad you did. Furhter means that if you change this file outside of touch – say in a text editor like Sublime or Visual Studio Code, this TOX module will watch to see if that file has changed, and if it has pulse reload the operator that’s referencing that file. Better still, if it’s an extension, the parent() operator will have its extensions reinitialized. There’s a little set-up and convention required there, but well worth it if you happen to use extension on a regular basis.

Parameters

base save and pars

Extension Flag

The Extension Flag is the tag you will add to any text DAT that you’re using as an extension. This ensures that we can easily identify which text DATs are being used as externally edited extensions, and reload both the contents of the DAT, as well reinitialize the extensions for the parent() operator. You can use any descriptor here that you like – I happen to think that something like EXT works well.

Log to Texport

If you want to track when and where your external files are being saved, or if you’re worried that something might be going wrong, you can turn on the Logtotextport parmeter to see the results of each save operation logged for easy view and tracking.

Default Color

The default color is set as a read-only parameter used to reset the network worksheet background color. This is used in conjunction with the following two parameters to provide visual indicators for when a save or load operation has happened.

BG Color

This is the color that the network background will flash when you externalize a TOX – it’s the visual indicator that your tox has been sucessfully saved.

Save Color

This is the color that the network background will flash save a text based file in an external editor – it’s the visual indicator that your file has been reloaded.

EXT Color

This is the color used to set the node color of your newly externalized tox – this can help ensure that at a glance you can tell which operators have been externalized.

Version

The version number for this tool.

Operation

reinitextensions.pulse()

If you want to use this in conjunction with extensions, you’ll need to follow a few conventions:

  • The text DAT that references an extension needs to be inside of the COMP uses it as an extension. For example – let’s say you have a text DAT that holds an extension called Project, this needs to live inside of the COMP that is using it as an extension.
  • The file you’re editing needs to end in .py. This might seem obvious, but it’s important that the file you’re editing is a python file. There are a number of checks that happen to make sure that we don’t just reinit COMPs willy nilly, and this is one of those safety measures.
  • The text DAT holding the extension needs to be tagged EXT – or whatever Extension Flag you’ve set in the parameters for the TOX. This makes sure that we don’t just reinit the extensions of our parent every-time any .py file is saved, but only if the that file belongs is being read by a textDAT that’s marked as being an extension.

ctrl+s

The way you’ll use this tox is just as if you were working as you might normally. Only, when you hit ctrl + s, if you’re inside of a COMP that hasn’t been saved externally, you’ll be asked if you want to externalize that module. If you select yes you’ll next be asked where you want to save that module. This module will then create a folder that has the same name as your component, and save the tox inside of that folder (the tox will also have the same name as the component). Better yet, this module will auto-populate the path to the external tox with the location you’ve selected. When you press ctrl + s again it will warn you that you’re about to over-write your tox. If you confirm that you want to replace your tox, it will save the updated version right where your previous tox was located.

Using a text editor

If you’re using a text editor for supported externalized files, than work as you normally might. When you save your file in your text editor Touch will automatically reload the file in Touch. If your text DAT is tagged EXT it will also reinit the extensions of the text DAT’s parent().

Suggested Workflow

Externalization Only

  • Create a directory for your project
  • Open TouchDesigner and save your .TOE file in your new directory this is an important step – saving your project makes sure that the member project.folder correct points to your .TOE file.
  • Drop the base_save.tox from touchdesigner-save-external\release into your network – I’d recommend doing this at the root of your project, or in a place in your project specifically designed to hold other tools. I like to create a base called tools where I keep all the things that I use for development, or that any machine might need (meaning when you’re thinking on a single .TOE file that’s configured based on a machine’s role)
  • Create a new component, and navigate inside of this new COMP.
  • Use ctrl + s to save your project as you might usually.
  • Notice that you’re now prompted to save your COMP externally – select Yes
  • Create a new folder in your project folder called td-modules (this is my suggestion, though you can use any name you like). Navigate into this folder and compete the save process.
  • Check finder (macOS) or explorer (windows) to see that in td-moduels you now have a new directory for your tox, and inside of that directory is your saved tox file.
  • Notice that the color of your tox has changed so you know that it’s externalized.
  • Continue to work and save. Note that when you use ctrl+s both your project and your tox are saved. If you happen to create an external .TOX inside of a tox that’s already externalized, you’ll be prompted to save both the parent() and the current COMP or just the current COMP.

Using Git

  • Create a new repo
  • Clone / Initialize your repo locally
  • Open TouchDesigner and save your .TOE file in your repo
  • Drop the base_save.tox from touchdesigner-save-external\release into your network – I’d recommend doing this at the root of your project, or in a place in your project specifically designed to hold other tools. I like to create a base called tools where I keep all the things that I use for development, or that any machine might need (meaning when you’re thinking on a single .TOE file that’s configured based on a machine’s role)
  • Create a new component, and navigate inside of this COMP.
  • Use ctrl + s to save your project as you might usually.
  • Notice that you’re now prompted to save your COMP externally – select Yes
  • Create a new folder in your project folder called td-modules (this is my suggestion, though you can use any name you like). Navigate into this folder and compete the save process.
  • Check finder (macOS) or explorer (windows) to see that in td-moduels you now have a new directory for your tox, and inside of that directory is your saved tox file.
  • Notice that the color of your tox has changed so you know that it’s externalized.
  • Continue to work and save. Note that when you use ctrl+s both your project and your tox are saved. If you happen to create an external .TOX inside of a tox that’s already externalized, you’ll be prompted to save both the parent() and the current COMP or just the current COMP.
  • Commit and push your work.

External Text based files

  • Start by following the instructions above to set up your project with the base_save.tox
  • Create a folder in your project for scripts or modules.
  • Start by following the instructions above to set up your project with the base_save.tox
  • Create a folder in your project for scripts or modules.
  • Add a new text DAT to your network, right click and save externally.
  • Set path to your external file in your text DAT and turn on the load on start parameter
  • Start by following the instructions above to set up your project with the base_save.tox
  • Create a folder in your project for scripts or modules.
  • Add a new text DAT to your network, right click and save externally.
  • Set path to your external file in your text DAT and turn on the load on start parameter.
  • Now open your text file in your external editor and work directly with your text file. When you save your file you should see the background of TouchDesigner flash, and the contents of your text DAT reload.

External Extensions

  • Start by following the instructions above to set up your project with the base_save.tox
  • Follow the instructions above for externalizing a python file – this time, make sure you save your .py file inside of your tox’s folder, and make sure that the text DAT is inside of the component that will use the extensions.
  • Tag your text DAT with EXT or whatever extension flag you’ve chosen.
  • Set up a simple extension.
  • Start by following the instructions above to set up your project with the base_save.tox
  • Follow the instructions above for externalizing a python file – this time, make sure you save your .py file inside of your tox’s folder, and make sure that the text DAT is inside of the component that will use the extensions.
  • Tag your text DAT with EXT or whatever extension flag you’ve chosen.
  • Set up a simple extension.
  • Now open your extension in your external editor and work directly with your .py file. When you save your file you should see the background of TouchDesigner flash, the contents of your text DAT reload, and your extension will be reinitialized.

Additional Considerations and Suggestions

At this point, you might have guess that this kind of approach works best in well structured projects. Some suggestions for organization and approach:

  • Think about Order and Structure – while I’ve structured projects lots of different ways, it’s worth finding a file structure that you like and sticking with it. That might be a deeply nested structure (watch out that’ll bite you if you get too deep – at least on windows), or it might be something more flat. Regardless, think about a structure and stay with it.
  • Make Small Simple Tools – to the best of your ability, try to make sure your modules are independent islands. That’s not always possible, but if you can think carefully about creating dependencies, you’ll be happier for it. Use custom parameters on your components to keep modules independent from one another. Use select operators, or In’s and Out’s to build connenctions.
  • Reuse that TOX – while this approach is fancy and fun, especially when working with git, it’s also about making your future self happier. Thank carefully about how you might make something re-usable and portable to another project. THe more you can think through how to make pieces that can easily move from project to project the more time you can spend on the fun stuff… not on the pieces that are fussy and take lots of time.

An Example Project

In the folder called sample_project open the Sample_project.toe to see how this might work.

Credits

Inspired by the work of:

Anton Heestand and Willy Nolan
I’ve had the great fortune of working with both of these find developers. I regularly use an externalization tool authored by these two developers, and this TOX is partially inspired by their work. Many thanks for a tool that keeps on working and makes using GIT with TouchDesigner something that’s reasonable.

Icons

Material Design Icons by Google

TouchDesigner | Reusable Code Segmentation with Python

reusable-code-segmentation.PNG

Thinking about how to approach re-usability isn’t a new topic here, in fact there’s been plenty of discussion about how to re-use components saved as tox files, how to build out modular pieces, and how to think about using Extensions for building components you want to re-use with special functions.

That’s wonderful and exciting, but for any of us that have built deployed projects have quickly started to think about how to build a standard project that any given installation is just a variation of… a flavor, if you will, of a standard project build. Much of that customization can be handled by a proper configuration process, but there are some outliers in every project… that special method that breaks our beautiful project paradigm with some feature that’s only useful for a single client or application.

What if we could separate our functions into multiple classes – those that are universal to every project we build, and another that’s specific to just the single job we’re working on? Could that help us find a way to preserve a canonical framework with beautiful abstractions while also making space for developing the one-off solutions? What if we needed a solution to handle the above in conjunction with sending messages over a network?  Finally, what if we paired this with some thoughts about how we handle switch-case statements in Python? Could we find a way to help solve this problem more elegantly so we could work smarter, not harder?

Well, that’s exactly what we’re going to take a look at here.

First a little disclaimer, this approach might not be right for everyone, and there’s a strong assumption here that you’ve got some solid Python under your belt before you tackle this process / working style. If you need to tool up a little bit before you dig in, that’s okay. Take a look at the Python posts to help get situated then come back to really dig in.


Getting Set-up

In order to see this approach really sing we need to do a few things to get set-up. We’ll need a few operators in place to see how this works, so before we dig into the python let’s get our network in order.

First let’s create a new base:

base.PNG

Next, inside of our new base let’s set up a typical AB Deck of TOPs with a constant CHOP to swap between them:

typical-ab-deck.PNG

Above we have two moviefilein TOPS connected to a switch TOP that’s terminated in a null TOP. We also have a constant CHOP terminated in a null whose chan1 value is exported to the index parameter of our switch TOP.

Let’s also use the new Layout TOP in another TOP chain:

layout-top.PNG

Here we have a single layout TOP that’s set-up with an export table. If you’ve never used DAT Exports before you might quickly check out the article on the wiki to see how that works. The dime tour of that ideal is that we use a table DAT to export vals to another operator. This is a powerful approach for setting parameters, and certainly worth knowing more about / exploring.

Whew. Okay, now it’s time to set up our extensions. Let’s start by creating a textDAT called messageParserEXT, generalEXT, and one called jobEXT.

parser-general-job.PNG


The Message Parser

A quick note about our parser. The idea here is that a control machine is going to pass along a message to a set of other machines also running on the network. We’re omitting the process of sending and receiving a JSON blob over UPD, but that would be the idea. The control machine passes a JSON message over the network to render nodes who in turn need to decode the message and perform some action. We want a generalized approach to sending those blobs, and we want both the values and the control messages to be embedded in that JSON blob. In our very simple example our JSON blob has only two keys, messagekind and vals:

message = {
        'messagekind' : 'some_method_name',
        'vals' : 'some_value'
}

In this example, I want the messagekind key to be the same as a method name in our General or Specific classes.

Pero, like why?!

Before we get too far ahead of ourselves, let’s first copy and past the code below into our messageParserEXT text DAT, add our General and Specific Classes, and finish setting up our Extensions.

The General Code Bits

In our generalEXT we’re going to create a General class. This works hand in hand with our parser. The parser is going to be our helper class to handle how we pass around commands. The General class is going to handle anything that we need to have persist between projects. The examples here are not representative of the kind of code you’d have your project, instead they’re just here to help us see what’s happening in this approach.

The Specific Code Bits

Here in our Specific class we have the operations that are specific to this single job – or maybe they’re experimental features that we’re not ready to roll into our General class just yet, regardless, these are methods that don’t yet have a place in our canonical code base. For now let’s copy this code block into our jobEXT text DAT.

At this point we’re just about ready to pull apart what on earth is happening. First let’s make sure our extension is correctly set-up. Let’s go back up a level and configure our base component to have the correct path to our newly created extension:

 

reusable-ext-settings.PNG

Wait wait wait… that’s only one extension? What gives? Part of what we’re seeing here is inheritance. Our Specific class inherits from our General class, which inherits form our MessageParser. If you’re scratching your head, consider that a null TOP is also a TOP is also an OP. In the same way we’re taking advantage of Python’s Object oriented nature so we can treat a Specific class as a special kind of General operation that’s related to sending messages between our objects. All of his leads me to believe that we should really talk about object oriented programming… but that’s for another post.

Alright… ALMOST THERE! Finally, let’s head back inside of our base and create three buttons. Lets also create a panel execute for each button:

buttons.PNG

Our first panel execute DAT needs to be set up to watch the state panel value, and to run on Value Change:

change-switch.PNG

Inside of our panel execute DAT our code looks like:

# me - this DAT
# panelValue - the PanelValue object that changed# # Make sure the corresponding toggle is enabled in the Panel Execute DAT.
def onOffToOn(panelValue):
    return
def whileOn(panelValue):
    return
def onOnToOff(panelValue):
    return
def whileOff(panelValue):
    return
def onValueChange(panelValue):
    message = {
        'messagekind' : 'Change_switch',
        'vals' : panelValue } 
    parent().Process_message(message)
    return

If we make our button viewer active, and click out button we should see our constant1 CHOP update, and our switch TOP change:

switch-gif.gif

AHHHHHHHHH!

WHAT JUST HAPPENED?!


The Black Magic

The secret here is that our messagekind key in our panel execute DAT matches an existing method name in our General class. Our ProcessMessage() method accepts a dictionary then extracts the key for messagekind. Next it checks to see if that string matches an existing method in either our General or Specific classes. If it matches, it then calls that method, and passes along the same JSON message blob (which happens to contain our vals) to the correct method for execution.

In this example the messagekind key was Change_switch(). The parser recognized that Change_switch was a valid method for our parent() object, and then called that method and passed along the message JSON blob. If we take a look at the Change_switch() method we can see that it extracts the vals key from the JSON blob, then changes the constant CHOP’s value0 parameter to match the incoming val.

This kind of approach let’s you separate out your experimental or job specific methods from your tried and true methods making it easier in the long run to move from job to job without having to crawl through your extensions to see what can be tossed or what needs to be kept. What’s better still is that this imposes minimal restrictions on how we work – I don’t need to call a separate extension, or create complex branching if-else trees to get the results I want – you’ll also see that in the MessageParser we have a system for managing elegant failure with our simple if hasattr() check – this step ensure that we log that something went wrong, but don’t just throw an error. You’d probably want to also print the key for the method that wasn’t successfully called, but that’s up to you in terms of how you want to approach this challenge.

Next see if you can successfully format a message to call the Image_order() method with another panel execute.

What happens if you call a method that doesn’t exist? Don’t forget to check your text port for this one.

If you’re really getting stuck you might check the link to the repo below so you can see exactly how I’ve set this process up.

If you got this far, here are some other questions to ponder:

  • How would you  use this in production?
  • What problems does this solve for you… does it solve any problems?
  • Are there other places you could apply this same idea in your projects?

At the end of the day this kind of methodology is really looking to help us stop writing the same code bits and bobs, and instead to figure out how to build soft modules for our code so we can work smarter not harder.

With any luck this helps you do just that.

Happy Programming.


Take a look at the sample Repo for this example on Github:
touchdesigner-reusable-code-segmentation-python