Tutorial: Building Dynamic Workflows
Step-by-step guide to combining velocity curves and conditional actions for powerful context-aware MIDI controller mappings.
Overview
This tutorial will teach you to create sophisticated, context-aware workflows that adapt to:
- Time of day (work hours vs evening)
- Active application (DAW vs browser vs IDE)
- Day of week (weekdays vs weekends)
- Current mode
- How hard you hit the pad
By the end, you’ll build a complete workflow that transforms your MIDI controller into an intelligent assistant.
Prerequisites
- Conductor installed and configured
- Basic TOML editing skills (or use the GUI)
- A MIDI controller connected
- Understanding of basic triggers and actions
Estimated Time: 30-45 minutes
Tutorial Structure
We’ll build three progressively complex workflows:
- Beginner: Time-based app launcher
- Intermediate: Velocity-sensitive DAW control
- Advanced: Multi-condition workflow with nested logic
Workflow 1: Time-Based App Launcher (Beginner)
Goal: Launch Slack during work hours, Discord after hours.
Step 1: Create the Basic Mapping
Open your config.toml and add a new global mapping:
[[global_mappings]]
description = "Smart communication launcher"
[global_mappings.trigger]
type = "Note"
note = 8 # Choose your pad number
Step 2: Add the Conditional Action
Add the conditional logic:
[global_mappings.action]
type = "Conditional"
[global_mappings.action.condition]
type = "And"
conditions = [
{ type = "TimeRange", start = "09:00", end = "17:00" },
{ type = "DayOfWeek", days = [1, 2, 3, 4, 5] }
]
[global_mappings.action.then_action]
type = "Launch"
app = "Slack"
[global_mappings.action.else_action]
type = "Launch"
app = "Discord"
Step 3: Test the Workflow
- Save
config.toml - Config will hot-reload automatically
- Press your configured pad during work hours (Mon-Fri 9am-5pm)
- Verify Slack launches
- Press the same pad outside work hours
- Verify Discord launches
Understanding the Logic
IF (time is 9am-5pm AND day is Monday-Friday):
Launch Slack
ELSE:
Launch Discord
GUI Configuration
If using the GUI:
- Open Mappings view
- Click “Add Mapping”
- Set trigger to Note 8
- Select action type “Conditional”
- Select condition type “And”
- Add two sub-conditions:
- TimeRange: 09:00 to 17:00
- DayOfWeek: Select Mon-Fri
- Configure then_action: Launch → Slack
- Configure else_action: Launch → Discord
- Save
Workflow 2: Velocity-Sensitive DAW Control (Intermediate)
Goal: Control Logic Pro with velocity-sensitive MIDI notes, boosting soft hits for expressive playing.
Step 1: Create the Mapping
[[modes.mappings]]
description = "Expressive MIDI control"
[modes.mappings.trigger]
type = "Note"
note = 1
Step 2: Add Velocity Curve
Make soft hits more audible:
[modes.mappings.velocity_mapping]
type = "Curve"
curve_type = "Exponential"
intensity = 0.7 # Strong boost for soft notes
Step 3: Add Conditional DAW Control
Only send MIDI if Logic Pro is running:
[modes.mappings.action]
type = "Conditional"
[modes.mappings.action.condition]
type = "AppRunning"
app_name = "Logic Pro"
[modes.mappings.action.then_action]
type = "SendMidi"
port = "IAC Driver Bus 1"
message_type = "NoteOn"
channel = 0
note = 60 # Middle C
# Velocity is derived from mapped input velocity
[modes.mappings.action.else_action]
type = "Sequence"
actions = [
{ type = "Launch", app = "Logic Pro" },
{ type = "Delay", ms = 2000 },
{ type = "Text", text = "Logic Pro launched, try again" }
]
Step 4: Test the Workflow
-
With Logic Pro closed:
- Press pad → Logic Pro launches
- Wait 2 seconds → See notification
-
With Logic Pro running:
- Press pad softly → Sends MIDI note with boosted velocity
- Press pad hard → Sends MIDI note at full velocity
- Notice soft notes are more audible due to exponential curve
Understanding the Flow
Input Velocity → Exponential Curve (boost soft hits) → Mapped Velocity
IF Logic Pro is running:
Send MIDI with mapped velocity to IAC Driver
ELSE:
Launch Logic Pro
Wait 2 seconds
Show notification
Velocity Curve Effect
| Input Velocity | Without Curve | With Exponential (0.7) |
|---|---|---|
| 20 (very soft) | 20 | ~65 (much louder) |
| 64 (medium) | 64 | ~85 (slightly louder) |
| 100 (hard) | 100 | ~110 (barely affected) |
Workflow 3: Multi-Condition Smart Assistant (Advanced)
Goal: Create a single pad that adapts to time, app, and velocity for different actions.
The Scenario
We want pad 10 to:
- Work hours + Browser: Open new tab (velocity doesn’t matter)
- Work hours + DAW: Send velocity-sensitive MIDI
- Evening + Any app: Launch entertainment app based on velocity
- Soft hit: Launch Spotify
- Hard hit: Launch Steam
Step 1: Create the Foundation
[[global_mappings]]
description = "Adaptive smart pad"
[global_mappings.trigger]
type = "Note"
note = 10
Step 2: Configure Velocity Mapping
Use S-Curve for natural dynamics:
[global_mappings.velocity_mapping]
type = "Curve"
curve_type = "SCurve"
intensity = 0.5
Step 3: Build the Conditional Logic
First level: Check if work hours:
[global_mappings.action]
type = "Conditional"
[global_mappings.action.condition]
type = "And"
conditions = [
{ type = "TimeRange", start = "09:00", end = "17:00" },
{ type = "DayOfWeek", days = [1, 2, 3, 4, 5] }
]
Step 4: Work Hours Branch (Then Action)
During work hours, check which app is frontmost:
[global_mappings.action.then_action]
type = "Conditional"
[global_mappings.action.then_action.condition]
type = "Or"
conditions = [
{ type = "AppFrontmost", app_name = "Safari" },
{ type = "AppFrontmost", app_name = "Chrome" },
{ type = "AppFrontmost", app_name = "Firefox" }
]
[global_mappings.action.then_action.then_action]
type = "Keystroke"
keys = "t"
modifiers = ["cmd"] # New tab in browser
[global_mappings.action.then_action.else_action]
type = "Conditional"
condition = { type = "AppRunning", app_name = "Logic Pro" }
then_action = {
type = "SendMidi",
port = "IAC Driver Bus 1",
message_type = "NoteOn",
channel = 0,
note = 60
}
else_action = { type = "Text", text = "Work mode: Use browser or DAW" }
Step 5: Evening Branch (Else Action)
Outside work hours, use velocity to choose entertainment:
[global_mappings.action.else_action]
type = "VelocityRange"
soft_max = 64
soft_action = { type = "Launch", app = "Spotify" }
hard_action = { type = "Launch", app = "Steam" }
Complete Workflow
Here’s the full configuration:
[[global_mappings]]
description = "Adaptive smart pad: Browser/DAW at work, entertainment after hours"
[global_mappings.trigger]
type = "Note"
note = 10
[global_mappings.velocity_mapping]
type = "Curve"
curve_type = "SCurve"
intensity = 0.5
[global_mappings.action]
type = "Conditional"
[global_mappings.action.condition]
type = "And"
conditions = [
{ type = "TimeRange", start = "09:00", end = "17:00" },
{ type = "DayOfWeek", days = [1, 2, 3, 4, 5] }
]
# Work hours branch
[global_mappings.action.then_action]
type = "Conditional"
[global_mappings.action.then_action.condition]
type = "Or"
conditions = [
{ type = "AppFrontmost", app_name = "Safari" },
{ type = "AppFrontmost", app_name = "Chrome" },
{ type = "AppFrontmost", app_name = "Firefox" }
]
[global_mappings.action.then_action.then_action]
type = "Keystroke"
keys = "t"
modifiers = ["cmd"]
[global_mappings.action.then_action.else_action]
type = "Conditional"
condition = { type = "AppRunning", app_name = "Logic Pro" }
then_action = { type = "SendMidi", port = "IAC Driver Bus 1", message_type = "NoteOn", channel = 0, note = 60 }
else_action = { type = "Text", text = "Work mode: Use browser or DAW" }
# Evening branch
[global_mappings.action.else_action]
type = "VelocityRange"
soft_max = 64
soft_action = { type = "Launch", app = "Spotify" }
hard_action = { type = "Launch", app = "Steam" }
Testing the Advanced Workflow
Scenario 1: Monday 10am, Safari frontmost
- Press pad → New tab in Safari
Scenario 2: Tuesday 2pm, Logic Pro running
- Press pad softly → MIDI note with moderate velocity (S-curve mapped)
- Press pad hard → MIDI note with high velocity
Scenario 3: Friday 8pm
- Press pad softly → Spotify launches
- Press pad hard → Steam launches
Scenario 4: Monday 10am, VS Code frontmost
- Press pad → “Work mode: Use browser or DAW” notification
Understanding the Decision Tree
Input Velocity → S-Curve → Mapped Velocity
IF (weekday AND 9am-5pm):
IF (browser is frontmost):
New tab (Cmd+T)
ELSE IF (Logic Pro running):
Send MIDI note (mapped velocity)
ELSE:
Show notification
ELSE (evening/weekend):
IF (soft hit, velocity ≤ 64):
Launch Spotify
ELSE (hard hit, velocity > 64):
Launch Steam
Best Practices
1. Start Simple, Build Complexity
Don’t try to build the advanced workflow first. Start with:
- Single condition
- Add second condition with And
- Add nested conditional
- Add velocity mapping
2. Test Each Layer
After adding each condition:
- Save config
- Test the new behavior
- Verify existing behavior still works
- Add next layer
3. Use Descriptive Mappings
description = "Work hours: Browser tab OR DAW MIDI | Evening: Spotify/Steam"
Clear descriptions help when debugging.
4. Debug with Text Actions
If behavior is unexpected, temporarily replace actions with Text to see which path executes:
then_action = { type = "Text", text = "THEN path executed" }
else_action = { type = "Text", text = "ELSE path executed" }
5. GUI vs TOML
- Simple conditionals: Use GUI for visual editing
- Nested logic: Use TOML for precision and readability
- Velocity curves: Use GUI for real-time preview graph
Common Patterns
Pattern 1: App-Specific Shortcuts
condition = { type = "AppFrontmost", app_name = "MyApp" }
then_action = { type = "Keystroke", keys = "s", modifiers = ["cmd"] }
else_action = { type = "Text", text = "Not in MyApp" }
Pattern 2: Time-Based Behavior
condition = { type = "TimeRange", start = "22:00", end = "08:00" }
then_action = { type = "Text", text = "Quiet hours - action disabled" }
else_action = { /* normal action */ }
Pattern 3: Launch-or-Control
condition = { type = "AppRunning", app_name = "MyDAW" }
then_action = { /* control command */ }
else_action = { type = "Launch", app = "MyDAW" }
Pattern 4: Velocity-Gated Actions
[velocity_mapping]
type = "Linear"
min = 50
max = 110
[action]
type = "VelocityRange"
soft_max = 70
soft_action = { /* gentle action */ }
hard_action = { /* aggressive action */ }
Troubleshooting
Problem: Condition Never True
Check:
- Time format is
HH:MM(24-hour) - App name matches exactly (check Activity Monitor on macOS)
- Day numbers are correct (Monday=1, not 0)
- Platform support (AppFrontmost is macOS-only)
Debug:
# Add debug text to both branches
then_action = { type = "Text", text = "Condition TRUE" }
else_action = { type = "Text", text = "Condition FALSE" }
Problem: Wrong Action Executes
Check:
- Logical operator (And vs Or)
- Nesting structure (use proper TOML indentation)
- Short-circuit evaluation (And stops at first false, Or at first true)
Debug:
- Test each sub-condition individually
- Simplify nested logic to isolate issue
Problem: Velocity Curve Doesn’t Feel Right
Solution:
- Open GUI velocity curve preview
- Adjust intensity in 0.1 increments
- Try different curve types:
- Exponential: Boost soft hits
- Logarithmic: Tame hard hits
- S-Curve: Natural feel with sweet spot
Next Steps
Expand Your Workflows
Now that you’ve mastered dynamic workflows, try:
- Per-App Profiles: Different mappings for different apps
- Mode-Based Logic: Use ModeIs condition for mode-specific behavior
- Sequence Actions: Chain multiple actions with delays
- Advanced Velocity: Combine curves with VelocityRange triggers
Share Your Workflows
Export your config.toml and share with the community:
- GitHub discussions
- Reddit r/midicontrollers
- Discord servers
Learn More
- Guide: Context-Aware Mappings - Deep dive into conditionals
- Guide: Velocity Curves - Master velocity mappings
- Configuration: Actions - All available actions
- Configuration: Triggers - All available triggers
Summary
You’ve learned to:
- ✅ Combine time and app conditions with And/Or logic
- ✅ Use velocity curves for expressive control
- ✅ Build nested conditional logic
- ✅ Create context-aware workflows
- ✅ Test and debug complex mappings
- ✅ Follow best practices for maintainable configs
Your MIDI controller is now an intelligent, context-aware assistant that adapts to your workflow automatically.
Example Configs: See examples/ directory for complete workflow examples:
work-productivity.toml- Time-based work/personal splitdaw-control.toml- Velocity-sensitive music productiongaming-streams.toml- Entertainment and streaming control