Track and Sequencing Python Samples
This guide demonstrates how to use the Disguise Python API to work with the Track system, including tracks, layers, field sequences, and key sequences. These concepts are central to timeline-based animation, automation, and show control in Disguise.
Track Operations
| Task | Python |
|---|---|
| Get the current track | track = guisystem.trackReturns the main Track object for the current GUI system. |
| Get the current playhead position (in beats) | current_beat = guisystem.player.tCurrent |
| Get the track’s total length (in beats) | track_length = track.lengthInBeats |
| Convert a time in seconds to beats | beats = track.timeToBeat(seconds)Time vs. beats can vary across the track - it is not always a fixed relationship. |
| Convert beats to seconds | seconds = track.beatToTime(beats) |
| Length in beats from a duration in seconds | length_beats = track.timeToBeat(start_seconds + duration_seconds) - start_beatsUseful for layer duration when creating a new layer. |
Cues
Direct access to cues is possible via track.cues, which returns a list of all Cue objects on the track. However, it is often easier to work with the Track methods for sections, tags and notes directly.
Cues are unnamed Resources.
Tags
- Tags are markers placed at specific beats on the track. They are useful for navigation, triggering externally, or marking important events.
- There are special tag types:
CUEis used for specific cue numbering, and follows the schema X, X.Y or X.Y.Z (egCUE 1.2.3)TCis used to represent a Timecode reference, in the format HH:MM.ff - refer to the track frame rate to ensure you set this appropriately (egTC 12:34:56.00)MIDIindicates external trigger from a MIDI “note on” event. Supports a dotted notation (<channel>.<note>) to specify a specific MIDI note on a specific channel (egMIDI 1.60for channel 1, note 60).
- Tags can be added, removed, or queried. Each tag typically has a beat position and a string label.
- The list of all tags is accessible via the
track.tagsproperty.
Notes
- Notes are text annotations that can be attached to specific beats or sections. They are useful for adding comments, reminders, or cues for operators and programmers.
- Notes can be set or retrieved using the appropriate methods on the track or section objects.
Layer Operations
A Layer represents a composable element on the Track. Layers are unnamed Resources.
| Task | Python |
|---|---|
| Iterate through all layers on a track | for layer in track.layers: |
| Get the number of layers | num_layers = len(track.layers) |
| Find a specific layer by name | my_layer = next((l for l in track.layers if l.name == "My Layer"), None) |
| Add a new layer to the track | track.addNewLayer(ModuleType, start_beats, length_beats, "Layer Name")Creates a new layer of the given module type, setting the time extents using start & length. |
| Get the module type of a layer | layer.moduleType() |
| Remove a layer from the track | track.removeLayer(layer) |
| Reorder layers | track.moveLayer(layer, new_index) |
| Get a layer’s name | layer.name NOTE - Layer is an exception where name is used instead of description |
| Set a layer’s name | layer.name = "New Name" |
| Get a layer’s start time (in beats) | layer.tStart |
| Set a layer’s start time | layer.tStart = 16.0 |
| Get a layer’s duration (in beats) | layer.tLength |
| Set a layer’s duration | layer.tLength = 8.0 |
| Enable/disable a layer | layer.enabled = True or layer.enabled = False |
| Arrow from src into dst | track.makeArrow(src_layer, dst_layer) |
Layer time extents
The most important Layer properties are their time extents: tStart, tEnd and tLength (which is tEnd - tStart). All of these values are in track beats.
The time extents determine when a Layer is active on the Track. When creating or modifying layers, it’s important to set the time extents carefully to avoid unexpected behavior.
Best practices for time extents:
- Match related content: When creating layers that work together (e.g., a blur effect for specific video layers), match their time extents to the content they affect
- Avoid arbitrary full-track layers: Creating a layer that spans the entire track makes it difficult to understand what’s active at any given time
- No unnecessary gaps: Don’t create gaps between related layers unless there’s a specific reason, as this can cause unintended pauses
- Context matters: Consider what the layer is doing and how it relates to other timeline elements when setting its duration
Example - Creating a layer that matches existing layers:
# Find the time range of target layers
target_layers = [l for l in track.layers if l.name.startswith("Video")]
layer_start_beats = min(l.tStart for l in target_layers)
layer_end_beats = max(l.tEnd for l in target_layers)
layer_length_beats = layer_end_beats - layer_start_beats
# Create a new layer with matching time extents
blur_layer = track.addNewLayer(BlurModule, layer_start_beats, layer_length_beats, "Blur Effect")
Example - Creating a timed layer:
# Create a 30-second layer starting at the current playhead
current_time = trackTime()
duration_seconds = 30
duration_beats = track.timeToBeat(current_time + duration_seconds) - track.timeToBeat(current_time)
video_layer = track.addNewLayer(VideoModule, track.timeToBeat(current_time), duration_beats, "Video")
Layer composition
Layers which occupy the same time range (tStart/tEnd) and use mappings which target the same screens compose on top of each other according to the blend modes the layers are set to. To control the blending order of the Layers, the order they appear in their parent (track or container).
The earlier in their parent, the higher in the composition stack the Layer is used, and a layer’s blend mode affects layers “below” it.
If the user references multiple layers, there are 2 scenarios to consider:
- The content has some known relationship (e.g. they target different mappings and are used together, or should be blended together): the layers should share some overlap in tStart/tEnd.
- The content is independent: the layers should be arranged separately on the track.
Note the above means that there is NO relationship between track position and position in the parent’s layer list! The first layer in the track can be the last item in the list.
Modules
Modules define the different types of layers and their behaviors within a track. Each module defines the specific properties and methods accessible to the Layer.
Do not access the Module object directly. It may be absent or change. Use ModuleConfig and FieldSequence on the Layer instead.
ModuleConfig
The ModuleConfig is accessible via layer.moduleConfig. For certain Module types, this can include specific settings or parameters that control the behavior of the layer. For example, OpenModule has a corresponding OpenModuleConfig which allows for customization of which system properties the Layer can access. The majority of Module types do not use ModuleConfig. It is used for modules that are very generic in nature and require additional configuration to provide dynamic FieldSequence properties on the Layer instance. ModuleConfigs are unnamed Resources.
FieldSequence & KeySequence
Layers are unnamed Resources. They contain field sequences, which represent the sequencable properties on the layer. This is the only way to apply values to Layers.
A KeySequence stores the actual keyframes for a property. All KeySequences have at least 1 keyframe.
Important: Keyframes have a time (t) value that represents a specific beat on the Track, not within the Layer. When setting keyframe times, always use Track time. For example: field_sequence.sequence.setResource(layer.tStart, <resource>).
In the examples below, fseq is a FieldSequence and keyseq is a KeySequence:
| Task | Python |
|---|---|
| Find a sequence for a property | fseq = layer.findSequence("brightness")Returns the FieldSequence for the “brightness” property. |
| List all sequences on a layer | sequences = layer.sequencesReturns a list of all FieldSequence objects. |
| Enable multi-keyframe sequencing for a property | fseq.disableSequencing = FalseOnly use if the value truly needs to change over time. |
| Check if a property is sequenced | is_sequenced = not fseq.disableSequencing |
| Evaluate a value at a track beat | fseq.eval(track_time_in_beats, default_value) |
Working with KeySequence in more detail:
| Task | Python |
|---|---|
| Get the KeySequence for a property | keyseq = fseq.sequence |
| Get all keyframe times | key_times = [keyseq.t(i) for i in range(keyseq.nKeys())]Returns a list of all keyframe times (in beats). |
| Get the number of keyframes | num_keys = keyseq.nKeys() |
| Get the value of a keyframe at a specific index | val = keyseq.key(i)Returns the Key object at index i. |
| Get the time of a keyframe at a specific index | t = keyseq.t(i)Returns the time (in beats) of the keyframe at index i. |
| Insert a keyframe | keyseq.insert(i, time_in_beats, key_type)Inserts a key of type key_type at index i and time time_in_beats. |
| Insert a keyframe with a KeyContainer | keyseq.insertKeyContainer(time_in_beats, i, key_container)Inserts a KeyContainer at time and index. |
| Remove a keyframe at a specific index | keyseq.remove(i, 1)Removes the keyframe at index i. |
| Clear all keyframes except the first | keyseq.stripToFirstKey() |
| Float Sequence at index | keyseq.key(i).v = value |
| Float Sequence at time | keyseq.setFloat(track_beat, value) |
| String Sequence at index | keyseq.setString(i, value) |
| String Sequence at time | keyseq.setStringAtT(track_beat, value) |
| Resource Sequence at index | keyseq.key(i).r = resource_obj |
| Resource Sequence at time | keyseq.setResource(track_beat, resource_obj) |
An example of setting a float sequence to a constant value:
brightness_fseq = my_layer.findSequence("brightness")
assert(brightness_fseq is not None)
# Disable sequencing - value not animating
brightness_fseq.disableSequencing = True
# Set the brightness value on the KeySequence
brightness_fseq.sequence.setFloat(my_layer.tStart, 25.0)
Searching for Media
You can find and reference media assets (like images, videos, or audio files) within your projects using the resource manager.
Finding Media by Name
The examples below show common patterns for finding media. Replace MediaType with the appropriate type (e.g., VideoClip, ImageResource, AudioFile).
| Task | Python |
|---|---|
| List all media assets in a project | all_media = resourceManager.allResources(MediaType) |
| Find the first media asset which contains ‘search’ | resource = next(r for r in resourceManager.allResources(MediaType) if 'search' in r.path.filename) |