MIDI mapping,
without the wiring.
TDMap is a TouchDesigner component for mapping any MIDI controller to any parameter — with a real-time web UI for knobs, faders, buttons, push encoders, and 14-bit controls across multiple devices and banks.
What's in TDMap.
Drag a parameter, move a knob, and you're mapped. Then layer behavior on top — across as many devices, banks, and recall scenarios as you need.
Controls
Every type of MIDI control TDMap recognizes out of the box.
Mapping
Get a controller talking to TD in seconds, then refine.
.tox, drag & drop params, move controls, go. No CHOP spaghetti, no expressions or binds — pure control.Behaviors
The reason TDMap exists isn't just to wire CC numbers — it's to layer behavior on top.
Polish
The control-feel layer — smoothing, response curves, range shaping.
Workflow
The little quality-of-life stuff that adds up.
TDMap_Config COMP outside the component. Drop in a newer .tox any time — every device, bank, and mapping is untouched.Preferences — what you can dial in click to expand
Open the gear icon in the toolbar to find these. All preferences persist across sessions in a single tdmap-prefs.json file next to your configs.
Defaults for new controls
| Default push mode | Assignment / Function / Slot / Modify — what a brand-new button or newly enabled push-knob starts in. If you mostly use buttons for actions, set this to Function. |
| Default function & value | When the default push mode is Function, also pick the function name (e.g. Reset, Randomize) and any value it needs. New buttons pre-fill with these. |
| Multi-assignment by default | New slots start in multi-assignment mode (one slot drives many pars). Off by default. |
| MIDI feedback by default | Newly created slots have MIDI Out feedback enabled. Useful if all your controllers have motorized faders or LED rings. |
Behavior
| Auto-toggle on assignment | When you assign a pulse / toggle / momentary par to a button, automatically pick the matching button action — so you don't have to think about it. |
| Smart Learn | When learning a control, watch the first few movements and auto-classify it as a knob / fader / encoder / button + the right MIDI protocol. |
| Auto-save layout | Persist every change to the global config the moment it happens — no manual save. |
| Protect read-only & disabled pars | Block MIDI value writes to parameters TouchDesigner marks as read-only or disabled (mapping stays, value writes don't fire). |
| Loop menus | Menu parameters wrap around at both ends when stepped past the edges (off = clamp). |
| MIDI-control string menus | Allow MIDI to drive string-style menus (StrMenu). Off by default since the values are arbitrary strings, not ordered. |
| Middle-click "show in pane" — floating | When on, middle-click always opens a floating network editor (don't follow the network editor pane you last used in TD). |
Feel
| Global filter | Default exponential-smoothing amount for new slots. Per-slot override available in the inspector. |
| Global step / step mode | Default relative-encoder step size and adaptive vs fixed step. Per-slot override available. |
| Global pickup | Default pickup-on-bank-change for new slots (absolute controls wait to match current value before taking over). |
Workflow
| Enable undo & group window | Toggle Ctrl+Z and set how long rapid changes collapse into one undo step (so a knob sweep is one undo, not 200). |
| Slot-learn hold time | How long you have to hold a slot button before "slot learn" mode arms. |
| Theme & zoom | Light / dark theme, and a CEF-safe zoom (CSS transform, not browser zoom) so the panel renders sharply in any TD pane size. |
| Interactive tour | Replay the onboarding walkthrough any time. |
Public interface — driving TDMap from your own scripts click to expand
The TDMap component is shipped locked so its extension methods aren't directly accessible from outside callers. Locking serves two purposes: keeping users from accidentally breaking internals on a live show, and making the package less appetizing to AI-training scrapers that crawl public TouchDesigner tox files for code to ingest.
To keep the component scriptable anyway, TDMap exposes a small set of public methods over a public-interface Text DAT. Each non-blank, non-#-prefixed line is parsed as a method call and run against the COMP — one call per line, comments allowed, per-line errors logged to the Textport. Drive that DAT however you like: Python .text = ... assignment, a Replicator emitting rows, an external tox piping commands.
Setup: open the TDMap COMP's custom parameters, go to the Integration page, and pulse Create Public Ext Interface. That creates the Text DAT next to the COMP, ready to edit or drive from another script.
Execution triggers: the lines run automatically whenever the DAT's text changes, and also whenever the COMP's Runpublic custom parameter is pulsed. Use the pulse path when you want to re-fire the same lines without re-writing them — e.g. from a button, a CHOP event, or another script.
Arguments are parsed as Python literals from text — strings, numbers, booleans, lists, dicts, keyword args all work. You cannot pass live TouchDesigner objects (no op('/...') handles, no parameter refs, no module access — eval runs in a restricted scope). Pass a path as a string instead: write '/proj/synthA', not op('/proj/synthA'). If you need a value from elsewhere in TD, resolve it in your driving script first, then write the resulting literal into the DAT.
The intended pattern is to write into the Text DAT from a script rather than typing by hand — that way you compose lines once in real Python, with normal references and helpers, and only ship the final text to the DAT. For example, a DAT Execute attached to your project's state could push fresh navigation calls into TDMap whenever a scene change happens:
# In a script anywhere in your project tdmap_input = op('/proj/tdmap_input') # the Text DAT TDMap watches synth_path = synth_comp.path # resolve refs HERE, not in the DAT tdmap_input.text = f''' # Re-point all mappings at the new synth COMP RetargetBanks('/proj/synthA', '{synth_path}', device='*', banks='*') SelectDevice('knobs') SelectBank(0) '''.strip()
Example lines (what ends up in the DAT):
# Open a specific device + bank programmatically SelectDevice('knobs') SelectBank(2) # Retarget mappings after renaming a COMP RetargetBanks('/proj/synthA', '/proj/synthB', device='*', banks='*') # Auto-load a stored layout on startup SetDefaultGlobalConfig('live_set.json')
Methods
| Mapping | |
|---|---|
RetargetBanks | Find/replace operator-path prefixes across bank tables. find_prefix is a literal substring or, if it contains * ? [, a tdu.match pattern matched segment-by-segment against the leading /-segments of each op_path (e.g. '/noise*' → '/noise1'). device and banks are tdu.match patterns (None = active, '*' = all). |
Learn | Toggle global MIDI learn mode. None toggles, True/False set explicitly. |
| Navigation | |
SelectDevice | Switch the active device. |
SelectBank | Switch the active bank. device=None = active device. |
| Library | |
LoadGlobalConfig | Replace the entire TDMap state with a saved bundle. |
SaveGlobalConfig | Persist current state to a bundle. |
SetDefaultGlobalConfig | Mark a bundle as the auto-restore default on TDMap init. |
| Lifecycle | |
CreateDevice | Add a new TDMap device (name, channel, midi_id). |
RemoveDevice | Delete a device by name (irreversible). |
CreateBank | Add a bank to a device; returns its idx. |
RemoveBank | Remove a bank by idx. |
| Feedback | |
SendAllFeedback | Re-send MIDI feedback (LED / motor / ring) for every slot on the active bank — useful after external state changes the engine didn't observe. |
| Debug | |
Test | Stub that just calls debug('test') — smoke-test the DAT wiring. |
Two tiers, two ways to get it.
Base covers everything you need for solid MIDI mapping. Pro unlocks behavioral layers — curves, smoothing, modifier modes, and function buttons.
Everything you need to map a controller and ship a show.
- ✓Multi-device & banks — independent grids, channel IDs, swap mapping sets per bank
- ✓Smart Learn & Learn All — auto-detect type and protocol; learn an entire controller at once
- ✓Drag & drop assignments — single & multi-parameter, move or copy across slots
- ✓Button actions — toggle, momentary, pulse, set-max — for gate, latch, and trigger hardware
- ✓Velocity mapping — pads and keys drive parameters continuously via velocity; configurable note-off behavior (hold, reset to min, reset to default)
- ✓Push-knob support — separate parameter assignment for the secondary button
- ✓Keyboard mapping — bind parameters to specific keys on a MIDI keyboard, with octave shift and channel filtering
- ✓MIDI Device Manager — edit TD's MIDI I/O table inline, rescan for hardware, live two-line log preview on right-click
- ✓14-bit, 2's complement, relative invert — protocol toggles per slot
- ✓Range, clamp, default — per-assignment overrides
- ✓Hover mode & copy/paste — paint mappings onto whatever you're hovering
- ✓Unassigned MIDI panel — capture homeless input and organize later
- ✓Config library + auto-save — device layouts, factory presets, global configs
- ✓Undo with grouping — Ctrl+Z, with configurable rapid-move grouping
- ✓Step overrides — per-slot step mode and size for relative encoders
- ✓Retarget — batch-replace operator paths across assignments, banks, or all devices
- ✓Five view modes — Detailed, Compact, Mini, List, and Values (live value bars between min/max)
- ✓Multi-device view — every device stacked in one scrollable layout, with collapsible per-device sections and a global bank strip that switches all devices at once
- ✓Input range remap — per-control window for hardware that doesn't reach the full 0–127 / 0–16383 MIDI range
- ✓Interactive tour — built-in walkthrough of every feature
Layer math, curves, and modes on top of any mapping.
- ✓Easing curves — 30+ functions, per-knob or per-parameter override (absolute controls)
- ✓Filter (smoothing) — exponential smoothing, global or per-slot
- ✓Pickup mode — absolute controls wait to match current value before adjusting
- ✓Function mode — buttons trigger reset, set value, set default, bank switch, par learn, custom callbacks
- ✓Slot mode (◈) — buttons & push-knobs hot-swap the active parameter of any other slot
- ✓Slot learn — hold a slot button and hover a parameter to learn its target
- ✓Modify mode (⊛) — mathematically modify another control's output: scale, add, divide, offset, limit, quantize
- ✓Alternate Knob — push-knob's encoder gets a second parameter mapping. Hold or toggle the push button to flip the encoder between its two assignments.
- ✓Callbacks — trigger custom callbacks from buttons / push with arguments
- ✓Default push mode — configurable default mode and function for new buttons and push-knobs
On the roadmap.
What's coming next — driven by patron support. Subscribing or buying isn't just access; it funds these.
Drop it in. Start mapping.
No setup wizard, no config files. Drag the .tox onto your project, tell TD which MIDI inputs to listen to, and you can start mapping immediately.
- Drag & drop
TDMap.toxonto the root of your project - Open TD's MIDI Device Mapper (Alt+D) and enable your controllers as inputs
- Map something: drag a TD parameter onto the TDMap button (or onto a card in the UI) and move a knob — or pick your controller from the supported devices list to load a pre-made layout
- Open the UI in a bigger window at
http://localhost:16669any time
# Three ways to map a control 1. Drag a TD parameter onto the TDMap button (top-right of TD UI) → move a knob → mapped. 2. Drag a TD parameter onto a card in the TDMap web UI. 3. Pick your controller from the supported devices list → load a ready-made layout.
Map something.
The demo runs entirely in your browser — no TouchDesigner required. Try every UI feature with mock data.
Open the Live Demo →