What is TDJSON??
Spoiler alert: Example file at the end!
The first reaction when I start talking about TDJSON is “what is that??” Most people haven’t heard of it, and the people who have, may not have even had a reason to really use it yet. I myself hadn’t used it first hand until very recently when I helped one of my Learn TouchDesigner HQ members with a question about how to make a preset system in TouchDesigner, and my mind went to TDJSON.
TDJSON is a relatively new feature in TouchDesigner and it is essentially a bunch of Python helper code when you need to convert things to and from JSON format (a nice structured way of saving a bunch of data). When I say “things”, what I mean more specifically is “native TouchDesigner objects.” And when I say “native TouchDesigner objects” I mean Python classes implemented in TouchDesigner such as Parameter class, which is the native Python object that holds all the information about a specific parameter. Right now, the main implementation of TDJSON is really focused on custom parameters, and mainly for the use case I’m about to present, which is that of a simple way of making presets using custom parameters and TDJSON. So let’s dive in (you can also follow along with the example project at the end)!
Step 1: Setting up your custom parameters
The first thing to know is that the wiki page for TDJSON is here. It is the main reference point for what you need for TDJSON. It’s a little bit sparse since the feature is still pretty new, so that’s where I come in. The second thing to know is that most of it caters to turning custom parameters into JSON structures quickly. You have different functions (read: options) for turning single parameters into JSON structures, turning a page of custom parameters to JSON, and even just flat out turning every custom parameter on a COMP into a JSON structure. Notice here it only works with custom parameters, so if you need to control built-in parameters, the trick would be similar to what I teach in my Project Architecture training where I make an empty Base COMP called “SETTINGS” and anything I want to control anywhere in the network is connected to a custom parameter I add to my SETTINGS component. So go ahead and first make a bunch of custom parameters you want to control.
Step 2: Dump everything to JSON
Once you have a COMP with a bunch of custom parameters we can move over the to Python scripting we need to do to quickly turn all those custom parameters (including their settings, values, etc) into a JSON structure.
import json # you need this line to get started TDJ = op.TDModules.mod.TDJSON # this bulk converts everything in the custom parameters to a dict # you need extraAttrs otherwise you don't save values by default json_obj = TDJ.opToJSONOp(op('container1'), extraAttrs=['val']) # then we dump the json into a text op('save_this_file').text = json.dumps(json_obj)
You can follow along my code comments, but I’ll step through them real quick as well. First thing we do is to import the built in Python json library. We’ll need this at the end of our script. The second thing we need to do is create a shortcut to accessing our TDJSON helper code. TDJSON is a module inside the depths of TouchDesigner right now. In the future there will probably be an easier way to directly access it, but for now we make a new variable called TDJ that references a global OP shortcut TDModules, goes to the modules of that operator, and grabs the TDJSON module.
The next thing we do is create a variable called json_obj which is going to hold the JSON structured data that TDJ will return to us. The value of this variable is created when we call the opToJSONOp() function by the TDJSON helper code. If you’ve ever done this before manually by accessing every parameter value individually and trying to make your own dictionary and then saving that, you’ll find this single line soooooooooooooooooooooooooooooooooooo much easier. We just call opToJSONOp() and as the first argument give it the COMP to get all the custom parameters from. But there’s a catch! By default (not a great default if you ask me…) the actual value of the parameter isn’t written into the JSON structure. So we have to provide the second argument named extraAttrs which allows us to feed in our own custom data in a list. So we make a list and the only thing we put in it is ‘val’, which as we know from our op().par.val Python usage, is the value of the parameter.
The last thing we do is reference a Text DAT where we will write out data into, and we use json.dump() from the built in Python library which just turns the JSON object into a string that we can save and move around a little bit more easily.
There you go! You just made a preset! Wow that was easy.
Step 3: Save the preset!
Now that you have your preset essentially saved as a string object. How you want to store and manage them can be up to you. You could just make a new Text DAT for each preset you want to save. You can append the strings as a new row in a Table DAT and keep a ton of presets all in one place. In this example, my group member was trying to save them to external files because the project is going to become a locked project file type of app that will be distributed (so the user can’t access and save the project file itself easily). So my approach to keep this dead simple is to use a File Out DAT, and you can name and save out the data as a .dat file. Then you can quickly use a File In DAT to load the .dat file back in. Whichever method you use, this is probably the easiest part of the process.
Step 4: Load the preset
Depending on how you setup your saving system, I’ll assume you have the data you need in hand now, and it’s time to load the preset back into place. The Python code we use for this looks like the below:
# tdjson needed again TDJ = op.TDModules.mod.TDJSON # the datToJson basically just turns the text back to a json object json_obj = TDJ.datToJSON(op('filein1')) # then we do the inverse and overwrite all the parameters from our json dictionary TDJ.addParametersFromJSONOp(op('container1'), json_obj)
Wow, again only a few lines!! Quick and painless. Let’s step through it all. The first thing we do is similar to before, we have to make shortcut so that we can more quickly get to the TDJSON helper code.
The second thing we do is make a variable named json_obj which will hold our JSON object once we load it from it’s string format and turn it back into a real Python object. To do this, we can use another helper function named datToJSON which as the name implies, allows us to provide it with a DAT that has our JSON string in it, and it will load it up and turn it into a Python object for us.
Then we just need to do our final line, which uses the TDJSON helper function addParametersFromJSONOp(). Don’t let the long name scare you. What this does is that it takes our previously created JSON object and implements it onto the OP we give it as the first argument. Notice the end of both our JSON-making function and this one had Op on the end. There are a different reading and writing helper functions that work in pairs. So the Op ones work together in this case, because one creates the structure that the other is expecting. Like I mentioned, we just give this helper function the operator we want to put the parameters into, and then the second argument is our JSON object. Just like that you can recall all the parameter values and states as they were when you created the JSON in the first place. Job done!
One thing to note, is that technically behind the scenes this addParametersFromJSONOp() actually “creates” the parameters again, but by default will “overwrite” the existing ones. You can read more about this in the documentation, but for our purposes it’s essentially just writing the values over since nothing else has changed since. But if you do end up changing some of the parameter layouts or custom parameter settings (like min or max or clamping), this would actually also overwrite all of those changes. So be careful!
Wrap up (Step 5: Profit)
I know this post was maybe a lot of words (or looks like it) but that’s only because I really wanted to explain everything in an easy to understand way. You could probably skip most of my paragraphs, look at the code + code comments, and in 5 minutes you’d be on your way to rocking your own preset system. It’s really as simple and straight forward as using the TDJSON helper functions that are provided to scrape all of the operators parameters and values, save them into a JSON structured string, then whenever you need them, use another set of provided TDJSON helper functions to put them back onto your operator. Hopefully my explanations give you some more behind the scenes information on why things are the way they are. Enjoy!