Skip to content

TouchDesigner + Claude

Updated: 2026-05*

This article was generated by Claude and some portions are still unverified.

1. Introduction

This lecture covers how to call Claude (Anthropic’s large language model) from TouchDesigner (hereafter TD) and integrate it into your work, targeting both Windows and macOS. Claude is an AI that takes natural-language instructions and produces text generation, image understanding, structured data, and more. Calling the Claude API from TD lets you fold “semantic judgment,” “linguistic responses,” and “context-sensitive parameter generation” — things that are awkward to express in a node graph — into your projects.

We will work through the API key setup and then step through representative usage patterns: text generation, Vision (image input), and Tool use (function calling). The prerequisites are a working knowledge of basic Python syntax and familiarity with TD’s Text DAT and Web Client DAT.

1.1 Topics covered

  • Obtaining an Anthropic API key and managing it safely via environment variables
  • Installing the anthropic Python SDK into a TD project
  • Calling the API without the SDK, using TD’s built-in Web Client DAT
  • Auto-generating parameters via structured (JSON) responses
  • Sending TOP images to Claude with the Vision feature
  • Applications of streaming responses and Tool use

1.2 References

The content here draws from the following sites and official documentation.

References:

2. Development environment

To use Claude from TD you need to (a) set up access to the Anthropic API and (b) choose how to call the API from TD. The first is the same on both OSes; the second is a choice between the SDK approach and the Web Client DAT approach.

2.1 Obtaining an Anthropic API key

Using the Claude API requires an Anthropic account and an API key.

  • Go to console.anthropic.com and create an account.
  • Add a minimal amount of credit from “Billing” (about USD 5 is plenty for the examples in this lecture).
  • Issue a new key from “API Keys.”
  • Save the resulting key (the string starting with sk-ant-).
  • The API key is only displayed in full on the screen immediately after issuance — be sure to copy it then.
  • Never write the API key directly into a TD project file, into a Text DAT body, or into any text under git management. If it leaks, an unrelated third party can rack up charges on your account.

2.2 Passing the key via an environment variable

The safest approach is to set the API key as an OS environment variable and read it from inside TD with os.environ.get('ANTHROPIC_API_KEY').

Windows setup

  • From the Start menu, search for “environment variables” → open “Edit environment variables.”
  • Add a new user environment variable:
    • Name: ANTHROPIC_API_KEY
    • Value: (the sk-ant-... you obtained)
  • Restart TD so it picks up the change.

macOS setup

  • In a terminal, open ~/.zshrc and append:
    • export ANTHROPIC_API_KEY="sk-ant-..."
  • Run source ~/.zshrc to apply it.
  • Launch TD from that same terminal with open -a TouchDesigner.
  • On Mac, launching TD by double-clicking the icon in Finder may not inherit the environment variables. To be sure, launch it from the terminal with open -a.

To verify the variable arrived, run this in TD’s Textport (Alt+T on Windows, ⌥+T on Mac):

import os
key = os.environ.get('ANTHROPIC_API_KEY')
print(key[:15] + '...' if key else 'not set')

A prefix like sk-ant-api03-... means success. If you see not set, the variable hasn’t reached TD — restart TD or change how you launch it.

2.3 Installing the anthropic SDK

There are two ways to call the Claude API from TD.

  • Method A: Use the official anthropic Python SDK. The code is simpler and the feature set is broader. Requires an additional install.
  • Method B: Hit the API directly with TD’s built-in Web Client DAT. No extra installs at all.

This lecture shows Method A in the first part of Chapter 3 and Method B at the end. If you go the SDK route, install with the steps below. If you want to skip the SDK to keep environment setup minimal, you can skip this section (optional).

Windows

  • Open Command Prompt and run:
cd C:\TD_Projects\my_project
"C:\Program Files\Derivative\TouchDesigner\bin\python.exe" -m pip install --target=python_libs anthropic

macOS

  • In a terminal, run the following (assuming you have a Homebrew Python that matches TD’s Python version):
cd ~/TD_Projects/my_project
/opt/homebrew/bin/python3.11 -m pip install --target=python_libs anthropic
  • For the macOS prerequisite (installing a parallel Python via Homebrew), see Chapter 2 of the companion lecture “TouchDesigner + Python.”
  • It is important that the install target is python_libs inside the project folder. TD will not pick up a global install.

Verify the install in Textport:

import sys
sys.path.append(project.folder + '/python_libs')
import anthropic
print(anthropic.__version__)

If a version number prints, you’re good.

3. Simple integration examples

Four minimum-viable examples in order. Sections 3.1–3.3 use Method A (SDK); 3.4 uses Method B (Web Client DAT).

3.1 Get a one-line response

The smallest possible example: ask Claude for a one-line reply.

  • Text DAT td_claude_hello
    • Open “Edit Contents…” and paste:
import sys, os
sys.path.append(project.folder + '/python_libs')
import anthropic

client = anthropic.Anthropic(api_key=os.environ.get('ANTHROPIC_API_KEY'))

msg = client.messages.create(
    model='claude-haiku-4-5-20251001',
    max_tokens=100,
    messages=[
        {'role': 'user', 'content': 'Explain TouchDesigner in one line.'}
    ]
)
print(msg.content[0].text)

Right-click the Text DAT → Run Script. After 1–3 seconds the response text should appear in the Textport.

  • We’re using claude-haiku-4-5-20251001. Haiku is the fast, low-cost light model — plenty for everything in this lecture. If you need higher-quality responses, switch to claude-sonnet-4-6; for top-tier quality, claude-opus-4-7.

3.2 Generate a poem and display it on a Text TOP

An example of showing the response on screen via a TOP.

Wire the nodes as follows.

  • Text DAT td_claude_poem (holds the response)
  • Text DAT td_claude_runner (the script that runs)
  • Text TOP text_display

Text TOP text_display settings

  • Text: op('td_claude_poem').text (Expression mode)
  • Font Size: 48
  • Resolution: 1280 × 720

Text DAT td_claude_runner script

import sys, os
sys.path.append(project.folder + '/python_libs')
import anthropic

client = anthropic.Anthropic(api_key=os.environ.get('ANTHROPIC_API_KEY'))

msg = client.messages.create(
    model='claude-haiku-4-5-20251001',
    max_tokens=200,
    messages=[
        {'role': 'user', 'content': 'Write a three-line poem on the theme of light and shadow. Return only the poem, no preamble or commentary.'}
    ]
)
op('td_claude_poem').text = msg.content[0].text

Each time you Run Script on td_claude_runner, a new three-line poem appears on the Text TOP.

  • API calls have a 1–3 second latency, so they’re not suitable for per-frame invocation. Drive them with events — button presses, audio-level thresholds, timers — instead.

3.3 Receive parameters as JSON

Claude’s strength is not only free-form text but its ability to return structured data on demand. Here we generate multiple parameter values at once from a text description of a scene’s mood.

  • Text DAT td_claude_mood
import sys, os, json
sys.path.append(project.folder + '/python_libs')
import anthropic

client = anthropic.Anthropic(api_key=os.environ.get('ANTHROPIC_API_KEY'))

prompt = '''
Return parameters describing the mood of a scene as JSON. No preamble, no commentary, no code-fence markers.
Adhere strictly to the following schema:
{
  "r": float 0.0-1.0,
  "g": float 0.0-1.0,
  "b": float 0.0-1.0,
  "speed": float 0.0-2.0,
  "noise_amount": float 0.0-1.0
}
r/g/b are the RGB values (0.0-1.0) of a representative color for the scene.
The theme this time is "an underwater cave at midnight."
'''

msg = client.messages.create(
    model='claude-haiku-4-5-20251001',
    max_tokens=300,
    messages=[{'role': 'user', 'content': prompt}]
)
params = json.loads(msg.content[0].text)

op('constant1').par.colorr = params['r']
op('constant1').par.colorg = params['g']
op('constant1').par.colorb = params['b']
op('lfo1').par.rate = params['speed']
op('noise1').par.amp = params['noise_amount']

Node setup:

  • Text DAT td_claude_mood
  • Constant TOP constant1
  • LFO CHOP lfo1
  • Noise TOP noise1

Swap the theme line (“an underwater cave at midnight”) for “a park on a summer afternoon,” “the deepest corner of an abandoned factory,” “a stone garden with falling cherry blossoms,” etc. — and you’ll get parameters tuned to each mood.

  • Occasionally Claude breaks the instruction and adds a preamble. json.loads then raises — wrap it in try/except, or move to a larger model (sonnet or above) for more stability.
  • You can also have Claude return HSV and convert to RGB on your side, but for a teaching example, asking for RGB directly gives the most predictable results.
  • For stricter control over structured responses, use the Tool use mechanism covered in 4.3 — it prevents schema violations at the API level.

3.4 Call the API without the SDK, via Web Client DAT

If you’d rather not install the anthropic SDK, TD’s built-in Web Client DAT can hit the API directly. Useful when you want to minimize environment setup at the distribution end, or skip Homebrew on Mac.

Wire the nodes as follows.

  • Table DAT headers (for request headers)
  • Web Client DAT webclient1
  • Text DAT response (to extract the response)

Contents of Table DAT headers (a two-column table — left column is the header name, right column is the value)

  • Row 1: x-api-key | (the API key value)
  • Row 2: anthropic-version | 2023-06-01
  • Row 3: content-type | application/json

Web Client DAT webclient1 settings

  • Request URL: https://api.anthropic.com/v1/messages
  • Request Method: POST
  • Request Headers DAT: headers
    • The exact parameter name varies by TD version (“Headers DAT,” “Header Table,” etc.). On the Web Client DAT’s parameter page, look for the DAT parameter that references header tables and point it at headers.
  • Request Body: the JSON string below.
{
  "model": "claude-haiku-4-5-20251001",
  "max_tokens": 200,
  "messages": [
    {"role": "user", "content": "Describe TouchDesigner in one sentence."}
  ]
}

Press the Web Client DAT’s Request pulse button — the request is sent and the response is stored in webclient1’s response table.

  • Writing the API key directly into a Table DAT means the key gets saved into the project file. For real-world use, inject the value at startup via a script, or supply it from a separate Text DAT that reads the environment variable.
  • The Web Client DAT approach has two weaknesses: streaming responses are awkward to handle, and Vision / complex Tool use require you to hand-write the body JSON. For serious use, we recommend the SDK approach.

4. Simple experiments toward real applications

Three experiments that put Claude to more active use inside TD.

4.1 Vision: send camera frames to Claude and react to its reply

Claude accepts image input. You can send Claude a webcam frame, or even the image TD is producing in real time, and get back a textual description or assessment.

Wire the nodes as follows.

  • Video Device In TOP videoin1
  • Resolution TOP resolution1
  • Text DAT td_claude_vision (analysis script)
  • Text DAT caption (holds the response)
  • Text TOP caption_display

Resolution TOP resolution1 settings

  • Input TOP: videoin1
  • Output Resolution: 512 × 512
  • Resolution: Custom Resolution

Text TOP caption_display settings

  • Text: op('caption').text (Expression mode)

Text DAT td_claude_vision script

import sys, os, base64
sys.path.append(project.folder + '/python_libs')
import anthropic

# Save the target TOP to a temp PNG and base64-encode it
top = op('resolution1')
tmp_path = project.folder + '/tmp_frame.png'
top.save(tmp_path)
with open(tmp_path, 'rb') as f:
    img_b64 = base64.b64encode(f.read()).decode('utf-8')
os.remove(tmp_path)  # delete the temp file once it's been read

client = anthropic.Anthropic(api_key=os.environ.get('ANTHROPIC_API_KEY'))
msg = client.messages.create(
    model='claude-haiku-4-5-20251001',
    max_tokens=200,
    messages=[{
        'role': 'user',
        'content': [
            {'type': 'image', 'source': {
                'type': 'base64',
                'media_type': 'image/png',
                'data': img_b64
            }},
            {'type': 'text', 'text': 'Describe what you see in haiku-style, 20 characters or fewer. Return only the poem.'}
        ]
    }]
)
op('caption').text = msg.content[0].text

Each Run Script captures that instant’s camera image, has Claude render it as haiku, and shows it on the Text TOP.

  • TOP.save() is synchronous. Calling it every frame will tank your frame rate — drive it from a Timer CHOP at one shot every few-to-many seconds.
  • 512 × 512 is plenty of resolution. Higher only raises API cost; response quality barely changes.

4.2 Stream the response and draw text incrementally

Normally Claude’s response arrives when generation completes, but in streaming mode you can receive tokens as they’re generated. This lets you generate a long passage and animate it across the screen as it’s being written.

  • Text DAT story (accumulator for the response text)
  • Text DAT td_claude_streamer (the script that runs)
  • Text TOP story_display

Text TOP story_display settings

  • Text: op('story').text (Expression mode)

Text DAT td_claude_streamer script

import sys, os
sys.path.append(project.folder + '/python_libs')
import anthropic

client = anthropic.Anthropic(api_key=os.environ.get('ANTHROPIC_API_KEY'))

op('story').text = ''  # clear before drawing

with client.messages.stream(
    model='claude-haiku-4-5-20251001',
    max_tokens=500,
    messages=[
        {'role': 'user', 'content': 'Write a short ghost story set at an unmanned train station, about 200 characters. Return only the prose.'}
    ]
) as stream:
    for chunk in stream.text_stream:
        op('story').text = op('story').text + chunk

When you run it, the text held in story grows in step with Claude’s generation rate. Because the Text TOP references story, the on-screen text fills in gradually.

  • This blocks TD’s main thread, so TD’s overall frame rate dips while streaming. For real use, see the official Python Threading docs and run it on a separate thread.

4.3 Tool use: let Claude operate TD operators

Tool use is a mechanism where you give Claude “a list of functions you can call,” and Claude calls them. By wiring this up, you can have Claude operate TD operators directly from a natural-language instruction.

  • Text DAT td_claude_tooluse
import sys, os
sys.path.append(project.folder + '/python_libs')
import anthropic

client = anthropic.Anthropic(api_key=os.environ.get('ANTHROPIC_API_KEY'))

tools = [{
    'name': 'set_constant_color',
    'description': "Change the color (RGB) of the scene's central Constant TOP",
    'input_schema': {
        'type': 'object',
        'properties': {
            'r': {'type': 'number', 'description': 'Red component, 0.0 to 1.0'},
            'g': {'type': 'number', 'description': 'Green component, 0.0 to 1.0'},
            'b': {'type': 'number', 'description': 'Blue component, 0.0 to 1.0'}
        },
        'required': ['r', 'g', 'b']
    }
}, {
    'name': 'set_noise_amount',
    'description': "Change the overall noise amount of the scene",
    'input_schema': {
        'type': 'object',
        'properties': {
            'amount': {'type': 'number', 'description': '0.0 (calm) to 1.0 (maximally agitated)'}
        },
        'required': ['amount']
    }
}]

user_prompt = "Make it sunset-colored, and the air a little grainy please."

msg = client.messages.create(
    model='claude-sonnet-4-6',
    max_tokens=500,
    tools=tools,
    messages=[{'role': 'user', 'content': user_prompt}]
)

for block in msg.content:
    if block.type == 'tool_use':
        args = block.input
        if block.name == 'set_constant_color':
            op('constant1').par.colorr = args['r']
            op('constant1').par.colorg = args['g']
            op('constant1').par.colorb = args['b']
        elif block.name == 'set_noise_amount':
            op('noise1').par.amp = args['amount']

Give the vague instruction “Make it sunset-colored, and the air a little grainy please.” and Claude responds with calls like set_constant_color(r=0.95, g=0.45, b=0.15) and set_noise_amount(amount=0.25), which the script then applies to the real parameters.

Add any functions you like to the tools array — set_lfo_rate, set_camera_zoom, play_audio_clip — and you have Claude as a “natural-language-driven performance operator.”

  • Tool use depends on the model’s reasoning ability. Haiku basically works, but picking the right tool from several based on context is more stable with sonnet or above.
  • Compared to the JSON-prompt approach in 3.3, Tool use enforces the schema at the Anthropic end, giving you much higher reliability on structured output.

5. Three fun ideas

5.1 Idea 1: The AI audience — Claude watching and critiquing your work

Use Vision to send TD’s own output (a Render TOP or a Window COMP capture) to Claude at a fixed interval. Set the prompt to “You are a contemporary art critic. Describe in two sentences your impression of the video work you are now seeing,” and you have an installation where Claude critiques your own TD piece, continuously.

  • Render TOP render1 (or whichever TOP is your final output)
  • Timer CHOP timer1 (30-second period)
  • CHOP Execute DAT dat_exec1 (callback on timer1’s Cycle)
  • Text DAT critic_log (accumulator for the critiques)
  • Text TOP critic_display (overlaid at the bottom of the screen)

In the CHOP Execute DAT’s onValueChange or onOffToOn, send the current frame of render1 to Claude and append the response to critic_log. Reuse the script from 4.1 and swap the text part for:

'You are a contemporary art critic. Describe in two sentences, with technical vocabulary, your impression of the video work you are now seeing.'

Accumulate responses into critic_log as a ring buffer, and the act of Claude’s commentary slowly piling up itself becomes part of the exhibit. Export as CSV after the show and you have a written record: “this work, as seen by Claude” (a nice extra).

5.2 Idea 2: A self-evolving scene — Claude designs the next 30 seconds

An extension of the JSON parameter generation in 3.3. Give Claude “the parameters of the previous scene” and “how long the current audience member has been standing here,” and ask how the next 30 seconds should be staged.

  • Timer CHOP scene_timer (fires on a 30-second period)
  • Text DAT td_claude_director (the directing script)
  • Animation COMP anim1 (interpolation target for the returned parameters)

The prompt for td_claude_director (sketch only — previous_params is a dict holding the parameters of the previous 30 seconds, dwell_time is the audience dwell time in seconds from a Timer CHOP or sensor, and schema is the JSON schema string reused from 3.3):

prompt = f'''
You are the director of a video installation.
The parameters of the previous 30 seconds were:
{json.dumps(previous_params, ensure_ascii=False)}

The audience member has now been standing in front of this work for {dwell_time} seconds.

For the next 30 seconds, in order not to bore the audience and not to overstimulate them, return the next scene's parameters as JSON conforming to the schema below. No commentary:
{schema}
'''

Feed the returned parameters into an Animation COMP as the interpolation target, and Claude becomes a scene that continually designs itself forward. You can write context-aware directing logic in natural language: ramp up intensity for an audience that lingers, calm things back down for the next person if someone leaves quickly (a nice extra).

  • At one call every 30 seconds (claude-haiku-4-5, roughly a few hundred to a thousand tokens input + output), the hourly cost is on the order of tens of US cents — varies with token count and model pricing.

5.3 Idea 3: Get Claude as a sounding board for your in-progress network

Finally, a use case for production support rather than the audience experience. Collect the composition of your currently open TD network (operator names, types, connections, key parameters) in Python and ask Claude “how can this network be made more interesting?”

  • Text DAT td_claude_advisor
import sys, os, json
sys.path.append(project.folder + '/python_libs')
import anthropic

# Collect info on operators directly under the parent COMP of this Text DAT.
# To target all of /project1, replace parent() with op('/project1').
ops_info = []
for c in parent().findChildren(depth=1):
    ops_info.append({
        'name': c.name,
        'type': c.OPType,
        'inputs': [i.name for i in c.inputs if i is not None]
    })

client = anthropic.Anthropic(api_key=os.environ.get('ANTHROPIC_API_KEY'))
msg = client.messages.create(
    model='claude-sonnet-4-6',
    max_tokens=1000,
    messages=[{
        'role': 'user',
        'content': f'''My TouchDesigner network currently in progress is as follows:
{json.dumps(ops_info, ensure_ascii=False, indent=2)}

Propose three changes that would make this network more interesting. For each, state briefly:
- Which operators to add or modify
- Why the change would make the expression more interesting.'''
    }]
)
print(msg.content[0].text)

Run it and three change proposals appear in the Textport. Pipe it through a Slack webhook and you have a partner that lives in your studio while you work. It’s a way out of the “I keep reaching for the same operators and making the same-looking output” cul-de-sac.

  • Make the network-info collection recursive and you can read inside child COMPs as well, at the cost of more tokens. Start with depth=1.

6. Summary

The key points for using Claude from TD.

Recommended models by use case:

  • Short responses, JSON parameter generation, haiku / short text: claude-haiku-4-5-20251001
  • Tool use, structural judgment, Vision responses: claude-sonnet-4-6
  • Critical performance transitions, long-form generation: claude-opus-4-7

Unlike nodes or CHOPs, Claude (at least at present) is not something you can “call every frame” — both latency and cost assume event-driven invocation, somewhere between every few seconds and every few tens of seconds. So at the design stage of your work, “when to ask Claude” and “what to ask” need to be folded into the scene design itself.

A workable order of study:

  • 3.1–3.2: experience “call it and receive a response.”
  • 3.3: get comfortable with parameter generation via JSON responses.
  • 4.1: experience image input with Vision.
  • 4.3: treat Claude as an active operator via Tool use.

If you fold Claude in as “a second director,” “the first audience,” or “a sounding board while making,” you’ll widen the semantic and linguistic range of expression — territory that a TD node graph alone can’t easily reach.