TouchDesigner Starter Article 8

Working with Time

How time works in TouchDesigner — absolute time, local component time, the Timer CHOP, LFO patterns, and syncing animation to audio BPM.

⏱ 12 min touchdesigner time animation LFO BPM Timer CHOP

Two Clocks: Absolute and Local

TouchDesigner has two notions of time that you will work with constantly.

Absolute time tracks the total duration since TouchDesigner launched. It keeps running regardless of whether anything is playing or paused. You access it through the global absTime object:

  • absTime.seconds — a float: total seconds elapsed since launch. At 60 FPS, this increments by approximately 0.01667 each frame.
  • absTime.frame — an integer: total frame count since launch.
  • absTime.rate — the current FPS setting of the project (e.g., 60.0).

Absolute time never resets and never pauses. This makes it ideal for animations that should run continuously and independently of playback state — a background texture that keeps evolving even when you stop the timeline.

Local (component) time is the time within a specific COMP. It can be started, stopped, rewound, and looped. Access it through the me.time object:

  • me.time.seconds — seconds elapsed in this component’s timeline.
  • me.time.frame — current frame in this component’s timeline.
  • me.time.start, me.time.end — the range of the component’s timeline.

Local time is what the Timeline at the bottom of the screen controls. When you press Play and Stop in the Timeline, you are controlling project1’s local time. If you create a sub-COMP with its own timeline and set it to loop independently, it gets its own me.time.

Driving Animation with absTime

The simplest form of time-based animation in TouchDesigner is a Python expression on a parameter:

absTime.seconds * speed

This translates time into a continuously increasing value at a rate determined by speed. Applied to the Translate Z parameter of a Noise TOP with a speed of 0.3, the noise slides slowly through 3D space.

For oscillating motion, use a trigonometric function:

math.sin(absTime.seconds * frequency) * amplitude

Where frequency is cycles per second (Hz) and amplitude is the peak deviation. A frequency of 1 produces one full oscillation per second. To centre the oscillation at a different value (rather than zero), add an offset:

math.sin(absTime.seconds * 0.5) * 0.4 + 0.5

This oscillates between 0.1 and 0.9 at 0.5 Hz. Useful for parameters like brightness where you want to stay within a positive range.

For a bouncy, non-linear oscillation, take the absolute value of a sine:

abs(math.sin(absTime.seconds * 1.5))

This gives a shape that touches 0 at every half period and peaks at 1 — like a bouncing ball.

The LFO CHOP

Writing math expressions directly in parameter fields works, but for rhythmic animations it is usually cleaner to use the LFO CHOP (Low Frequency Oscillator). The LFO CHOP produces a time-varying channel using standard waveform shapes and gives you dedicated parameters for each aspect of the motion.

Add an LFO CHOP (Tab → lfo → LFO CHOP). Its parameters on the LFO tab:

  • Type: Sine, Square, Ramp (sawtooth), Triangle, or Pulse. Each produces a characteristic shape.
  • Frequency: cycles per second. 1 = one cycle per second. 0.25 = one cycle every four seconds.
  • Amplitude: the peak value of the wave. The default of 1 produces a range of -1 to +1.
  • Offset: shifts the wave vertically. Setting Amplitude to 0.5 and Offset to 0.5 produces a wave from 0 to 1.
  • Phase: shifts the wave in time (0–1 maps to 0–360 degrees). Useful for phase-offsetting multiple LFO CHOPs to create staggered animations.

Export the LFO CHOP’s output to a TOP parameter (Ctrl+drag onto the parameter) and you have a hardware-style modulator with a clean UI for adjustment.

Stacking LFOs for Complexity

A single LFO produces regular, predictable motion. To create more organic animation, combine multiple LFOs:

  1. Add three LFO CHOPs at different frequencies: 0.17, 0.31, 0.7 Hz. (Using incommensurable, irrational-ish frequencies means they will not align for a very long time.)
  2. Connect all three into a Math CHOP set to Combine Channels: Add.
  3. Normalise the sum with another Math CHOP if needed.
  4. Export the result to a parameter.

The combined signal is neither regular nor fully random — it has the organic character of layered natural oscillations.

The Noise CHOP for Smooth Random

For genuinely random-feeling but smooth motion, use the Noise CHOP instead of a combination of LFOs.

Add a Noise CHOP. Key parameters:

  • Type: Sparse gives smooth Perlin-style randomness. `Random** (if available) is harsher.
  • Frequency: controls how rapidly the noise changes. Higher frequency = faster, more agitated movement.
  • Amplitude: peak deviation.
  • Seed: different seeds produce entirely different sequences. Change this to get a different random trajectory.

A Noise CHOP at frequency 0.2 and amplitude 0.3 produces a slowly wandering value — perfect for subtle camera shake or slow colour drift.

The Timer CHOP

When you need precise timing — count up to a duration, trigger an event at a specific moment, loop a sequence — the Timer CHOP is the right tool.

Add a Timer CHOP (Tab → timer → Timer CHOP). Key parameters:

  • Timer Length: the total duration to count (in seconds by default; the Units parameter can change this to frames or samples).
  • Play: a toggle or pulsed input that starts the timer.
  • Loop: if on, the timer restarts automatically when it reaches Timer Length.
  • Reset: pulses back to zero without changing play state.

The Timer CHOP outputs several channels you can use:

  • timer_fraction0 at start, 1 at end. Perfect for driving a fade-in effect.
  • timer_seconds — the elapsed time in seconds.
  • done — a single-sample pulse (value of 1 for exactly one frame) when the timer completes.
  • active1 while the timer is running, 0 otherwise.

Common pattern: Feed timer_fraction through a Lookup CHOP or a Math CHOP to apply an easing curve, then export the result to an opacity parameter for a smooth fade-in on cue.

The Beat CHOP: Syncing to Audio BPM

For live music-reactive visuals, the Beat CHOP detects the tempo of an incoming audio signal and produces rhythmically-aligned trigger pulses.

  1. Add an Audio Device In CHOP and confirm it is receiving signal (look at the waveform in its viewer).
  2. Wire the Audio Device In CHOP into a Beat CHOP.
  3. In the Beat CHOP’s parameters, select the Source as the incoming audio input.
  4. The Beat CHOP outputs channels including beat (a pulse on each detected beat), bar (a pulse on each bar boundary), and beat_fraction (0-1 ramp that resets on each beat).

Export beat_fraction to the Translate Z of a Noise TOP to make the noise texture scroll in sync with the music’s pulse.

Manual BPM: If you know the BPM and want deterministic sync without audio analysis, bypass the audio detection by setting the Beat CHOP’s Beat Source to Manual and entering a BPM value directly. This is useful in rehearsal when you do not have the live audio feed available.

The Constant CHOP + Count CHOP Pattern

For triggering effects based on counts — after N beats, at the Nth loop — combine a Constant CHOP (or a Beat CHOP’s beat channel) with a Count CHOP:

  1. Add a Beat CHOP to get a beat pulse channel.
  2. Wire it into a Count CHOP. Set Limit Type to Loop and Maximum to 4.
  3. The Count CHOP outputs a channel that steps 0, 1, 2, 3, 0, 1, 2, 3... on each beat.
  4. Wire this into a Select CHOP or use it in an expression to alternate between four different states every four beats.

This pattern underpins much of the “section-switching” logic in live visual performances.

Project FPS and Timeline Settings

The project frame rate is set in the Timeline at the bottom of the screen. Click the FPS number to edit it. Common values:

  • 60 FPS: the default and the best choice for smooth, GPU-driven real-time work.
  • 30 FPS: appropriate for video-based projects where source material is 30 FPS, or when the GPU cannot sustain 60.
  • 24 FPS: cinematic material.

Changing the FPS does not change absTime.seconds — one second is always one second. It does change absTime.frame and me.time.frame, which now count at the new rate.

If your project is running at 60 FPS but dropping to 45 or 50 under load, do not lower the FPS setting — instead, profile with the Performance Monitor (Ctrl+Shift+P) and optimise the heavy operators. Lowering FPS just makes the drop less visible, it does not fix the underlying issue.

The next tutorial covers saving your project and sharing it with others — .toe files, .tox components, and working with assets.