Skip to content

Instantly share code, notes, and snippets.

@kolotaev
Created July 4, 2025 16:34
Show Gist options
  • Save kolotaev/e29a28d0b4e6daca80deb02b2d1e7642 to your computer and use it in GitHub Desktop.
Save kolotaev/e29a28d0b4e6daca80deb02b2d1e7642 to your computer and use it in GitHub Desktop.
Mixxx LauchControl XL MK2
<?xml version='1.0' encoding='utf-8'?>
<MixxxControllerPreset mixxxVersion="" schemaVersion="1">
<info>
<name>Launch Control XL</name>
</info>
<controller id="LaunchControl">
<scriptfiles>
<file filename="lodash.mixxx.js" functionprefix=""/>
<file filename="midi-components-0.0.js" functionprefix=""/>
<file filename="LaunchControlXL.midi-script.js" functionprefix="LCXL"/>
</scriptfiles>
<controls>
<control>
<group>[EqualizerRack1_[Channel1]_Effect1]</group>
<key>parameter3</key>
<description>MIDI Learned from 454 messages.</description>
<status>0xB0</status>
<midino>0x0D</midino>
<options>
<normal/>
</options>
</control>
<control>
<group>[Channel1]</group>
<key>pregain</key>
<description>MIDI Learned from 219 messages.</description>
<status>0xB0</status>
<midino>0x10</midino>
<options>
<normal/>
</options>
</control>
<control>
<group>[Channel2]</group>
<key>pregain</key>
<description>MIDI Learned from 255 messages.</description>
<status>0xB0</status>
<midino>0x11</midino>
<options>
<normal/>
</options>
</control>
<control>
<group>[Master]</group>
<key>headGain</key>
<description>MIDI Learned from 278 messages.</description>
<status>0xB0</status>
<midino>0x13</midino>
<options>
<normal/>
</options>
</control>
<control>
<group>[EqualizerRack1_[Channel2]_Effect1]</group>
<key>parameter3</key>
<description>MIDI Learned from 513 messages.</description>
<status>0xB0</status>
<midino>0x14</midino>
<options>
<normal/>
</options>
</control>
<control>
<group>[EqualizerRack1_[Channel1]_Effect1]</group>
<key>parameter2</key>
<description>MIDI Learned from 387 messages.</description>
<status>0xB0</status>
<midino>0x1D</midino>
<options>
<normal/>
</options>
</control>
<control>
<group>[Channel1]</group>
<key>volume</key>
<description>MIDI Learned from 189 messages.</description>
<status>0xB0</status>
<midino>0x20</midino>
<options>
<normal/>
</options>
</control>
<control>
<group>[Channel2]</group>
<key>volume</key>
<description>MIDI Learned from 560 messages.</description>
<status>0xB0</status>
<midino>0x21</midino>
<options>
<normal/>
</options>
</control>
<control>
<group>[Master]</group>
<key>headMix</key>
<description>MIDI Learned from 176 messages.</description>
<status>0xB0</status>
<midino>0x23</midino>
<options>
<normal/>
</options>
</control>
<control>
<group>[EqualizerRack1_[Channel2]_Effect1]</group>
<key>parameter2</key>
<description>MIDI Learned from 443 messages.</description>
<status>0xB0</status>
<midino>0x24</midino>
<options>
<normal/>
</options>
</control>
<control>
<group>[Channel1]</group>
<key>cue_default</key>
<description>MIDI Learned from 2 messages.</description>
<status>0x80</status>
<midino>0x29</midino>
<options>
<normal/>
</options>
</control>
<control>
<group>[Channel1]</group>
<key>cue_default</key>
<description>MIDI Learned from 2 messages.</description>
<status>0x90</status>
<midino>0x29</midino>
<options>
<normal/>
</options>
</control>
<control>
<group>[Channel1]</group>
<key>pfl</key>
<description>MIDI Learned from 2 messages.</description>
<status>0x80</status>
<midino>0x2A</midino>
<options>
<normal/>
</options>
</control>
<control>
<group>[Channel1]</group>
<key>pfl</key>
<description>MIDI Learned from 2 messages.</description>
<status>0x90</status>
<midino>0x2A</midino>
<options>
<normal/>
</options>
</control>
<control>
<group>[Channel1]</group>
<key>loop_in</key>
<description>MIDI Learned from 2 messages.</description>
<status>0x80</status>
<midino>0x2C</midino>
<options>
<normal/>
</options>
</control>
<control>
<group>[Channel1]</group>
<key>loop_in</key>
<description>MIDI Learned from 2 messages.</description>
<status>0x90</status>
<midino>0x2C</midino>
<options>
<normal/>
</options>
</control>
<control>
<group>[EqualizerRack1_[Channel1]_Effect1]</group>
<key>parameter1</key>
<description>MIDI Learned from 761 messages.</description>
<status>0xB0</status>
<midino>0x31</midino>
<options>
<normal/>
</options>
</control>
<control>
<group>[Channel1]</group>
<key>playposition</key>
<description>MIDI Learned from 300 messages.</description>
<status>0xB0</status>
<midino>0x32</midino>
<options>
<normal/>
</options>
</control>
<control>
<group>[QuickEffectRack1_[Channel1]]</group>
<key>super1</key>
<description>MIDI Learned from 82 messages.</description>
<status>0xB0</status>
<midino>0x34</midino>
<options>
<normal/>
</options>
</control>
<control>
<group>[QuickEffectRack1_[Channel2]]</group>
<key>super1</key>
<description>MIDI Learned from 126 messages.</description>
<status>0xB0</status>
<midino>0x35</midino>
<options>
<normal/>
</options>
</control>
<control>
<group>[Channel2]</group>
<key>playposition</key>
<description>MIDI Learned from 196 messages.</description>
<status>0xB0</status>
<midino>0x37</midino>
<options>
<normal/>
</options>
</control>
<control>
<group>[EqualizerRack1_[Channel2]_Effect1]</group>
<key>parameter1</key>
<description>MIDI Learned from 1075 messages.</description>
<status>0xB0</status>
<midino>0x38</midino>
<options>
<normal/>
</options>
</control>
<control>
<group>[Channel2]</group>
<key>loop_in</key>
<description>MIDI Learned from 2 messages.</description>
<status>0x80</status>
<midino>0x39</midino>
<options>
<normal/>
</options>
</control>
<control>
<group>[Channel2]</group>
<key>loop_in</key>
<description>MIDI Learned from 2 messages.</description>
<status>0x90</status>
<midino>0x39</midino>
<options>
<normal/>
</options>
</control>
<control>
<group>[Channel2]</group>
<key>pfl</key>
<description>MIDI Learned from 2 messages.</description>
<status>0x80</status>
<midino>0x3B</midino>
<options>
<normal/>
</options>
</control>
<control>
<group>[Channel2]</group>
<key>pfl</key>
<description>MIDI Learned from 2 messages.</description>
<status>0x90</status>
<midino>0x3B</midino>
<options>
<normal/>
</options>
</control>
<control>
<group>[Channel2]</group>
<key>cue_default</key>
<description>MIDI Learned from 4 messages.</description>
<status>0x80</status>
<midino>0x3C</midino>
<options>
<normal/>
</options>
</control>
<control>
<group>[Channel2]</group>
<key>cue_default</key>
<description>MIDI Learned from 4 messages.</description>
<status>0x90</status>
<midino>0x3C</midino>
<options>
<normal/>
</options>
</control>
<control>
<group>[Channel1]</group>
<key>play</key>
<description>MIDI Learned from 2 messages.</description>
<status>0x80</status>
<midino>0x49</midino>
<options>
<normal/>
</options>
</control>
<control>
<group>[Channel1]</group>
<key>play</key>
<description>MIDI Learned from 2 messages.</description>
<status>0x90</status>
<midino>0x49</midino>
<options>
<normal/>
</options>
</control>
<control>
<group>[Channel1]</group>
<key>sync_enabled</key>
<description>MIDI Learned from 2 messages.</description>
<status>0x80</status>
<midino>0x4A</midino>
<options>
<normal/>
</options>
</control>
<control>
<group>[Channel1]</group>
<key>sync_enabled</key>
<description>MIDI Learned from 2 messages.</description>
<status>0x90</status>
<midino>0x4A</midino>
<options>
<normal/>
</options>
</control>
<control>
<group>[Channel1]</group>
<key>reloop_toggle</key>
<description>MIDI Learned from 2 messages.</description>
<status>0x80</status>
<midino>0x4B</midino>
<options>
<normal/>
</options>
</control>
<control>
<group>[Channel1]</group>
<key>reloop_toggle</key>
<description>MIDI Learned from 2 messages.</description>
<status>0x90</status>
<midino>0x4B</midino>
<options>
<normal/>
</options>
</control>
<control>
<group>[Channel1]</group>
<key>loop_out</key>
<description>MIDI Learned from 2 messages.</description>
<status>0x80</status>
<midino>0x4C</midino>
<options>
<normal/>
</options>
</control>
<control>
<group>[Channel1]</group>
<key>loop_out</key>
<description>MIDI Learned from 2 messages.</description>
<status>0x90</status>
<midino>0x4C</midino>
<options>
<normal/>
</options>
</control>
<control>
<group>[Channel1]</group>
<key>rate</key>
<description>MIDI Learned from 365 messages.</description>
<status>0xB0</status>
<midino>0x4D</midino>
<options>
<normal/>
</options>
</control>
<control>
<group>[Master]</group>
<key>crossfader</key>
<description>MIDI Learned from 490 messages.</description>
<status>0xB0</status>
<midino>0x51</midino>
<options>
<invert/>
</options>
</control>
<control>
<group>[Master]</group>
<key>headMix</key>
<description>MIDI Learned from 1778 messages.</description>
<status>0xB0</status>
<midino>0x53</midino>
<options>
<normal/>
</options>
</control>
<control>
<group>[Channel2]</group>
<key>rate</key>
<description>MIDI Learned from 582 messages.</description>
<status>0xB0</status>
<midino>0x54</midino>
<options>
<normal/>
</options>
</control>
<control>
<group>[Channel2]</group>
<key>loop_out</key>
<description>MIDI Learned from 2 messages.</description>
<status>0x80</status>
<midino>0x59</midino>
<options>
<normal/>
</options>
</control>
<control>
<group>[Channel2]</group>
<key>loop_out</key>
<description>MIDI Learned from 2 messages.</description>
<status>0x90</status>
<midino>0x59</midino>
<options>
<normal/>
</options>
</control>
<control>
<group>[Channel2]</group>
<key>reloop_toggle</key>
<description>MIDI Learned from 2 messages.</description>
<status>0x80</status>
<midino>0x5A</midino>
<options>
<normal/>
</options>
</control>
<control>
<group>[Channel2]</group>
<key>reloop_toggle</key>
<description>MIDI Learned from 2 messages.</description>
<status>0x90</status>
<midino>0x5A</midino>
<options>
<normal/>
</options>
</control>
<control>
<group>[Channel2]</group>
<key>sync_enabled</key>
<description>MIDI Learned from 2 messages.</description>
<status>0x80</status>
<midino>0x5B</midino>
<options>
<normal/>
</options>
</control>
<control>
<group>[Channel2]</group>
<key>sync_enabled</key>
<description>MIDI Learned from 2 messages.</description>
<status>0x90</status>
<midino>0x5B</midino>
<options>
<normal/>
</options>
</control>
<control>
<group>[Channel2]</group>
<key>play</key>
<description>MIDI Learned from 2 messages.</description>
<status>0x80</status>
<midino>0x5C</midino>
<options>
<normal/>
</options>
</control>
<control>
<group>[Channel2]</group>
<key>play</key>
<description>MIDI Learned from 2 messages.</description>
<status>0x90</status>
<midino>0x5C</midino>
<options>
<normal/>
</options>
</control>
<control>
<group>[Library]</group>
<key>MoveUp</key>
<description>MIDI Learned from 2 messages.</description>
<status>0xB0</status>
<midino>0x68</midino>
<options>
<normal/>
</options>
</control>
<control>
<group>[Library]</group>
<key>MoveDown</key>
<description>MIDI Learned from 2 messages.</description>
<status>0xB0</status>
<midino>0x69</midino>
<options>
<normal/>
</options>
</control>
<control>
<group>[Channel1]</group>
<key>LoadSelectedTrack</key>
<description>MIDI Learned from 2 messages.</description>
<status>0xB0</status>
<midino>0x6A</midino>
<options>
<normal/>
</options>
</control>
<control>
<group>[Channel2]</group>
<key>LoadSelectedTrack</key>
<description>MIDI Learned from 2 messages.</description>
<status>0xB0</status>
<midino>0x6B</midino>
<options>
<normal/>
</options>
</control>
</controls>
<outputs/>
</controller>
</MixxxControllerPreset>
var LCXL = new Controller();
// NOTE: All the messages assume we always work with template #1.
// For spec see:
// https://fael-downloads-prod.focusrite.com/customer/prod/s3fs-public/downloads/launch-control-xl-programmers-reference-guide.pdf
LCXL.COLORS = {
// red 00
Off: 0x0C,
GreenLow: 0x1C,
Green: 0x2C,
GreenHigh: 0x3C,
// red 01
RedLow: 0x0D,
AmberLow: 0x1D,
Amber: 0x2D,
AmberHigh: 0x3D,
// red 10
Red: 0x0E,
OrangeLow: 0x1E,
YellowLow: 0x2E,
Yellow: 0x3E,
// red 11
RedHigh: 0x0F,
OrangeHigh: 0x1F,
Orange: 0x2F,
YellowHigh: 0x3F,
};
LCXL.COLORS.Flashing = function (colorMessage) {
return colorMessage - 0x4;
}
LCXL.BUTTONS = {
Top1: 24,
Top2: 25,
Top3: 26,
Top4: 27,
Top5: 28,
Top6: 29,
Top7: 30,
Top8: 31,
Bottom1: 32,
Bottom2: 33,
Bottom3: 34,
Bottom4: 35,
Bottom5: 36,
Bottom6: 37,
Bottom7: 38,
Bottom8: 39,
Device: 40,
Mute: 41,
Solo: 42,
Arm: 43,
Up: 44,
Down: 45,
Left: 46,
Right: 47,
};
LCXL.POTS = {
High1: 0,
High2: 1,
High3: 2,
High4: 3,
High5: 4,
High6: 5,
High7: 6,
High8: 7,
Mid1: 8,
Mid2: 9,
Mid3: 10,
Mid4: 11,
Mid5: 12,
Mid6: 13,
Mid7: 14,
Mid8: 15,
Low1: 16,
Low2: 17,
Low3: 18,
Low4: 19,
Low5: 20,
Low6: 21,
Low7: 22,
Low8: 23,
};
LCXL.init = function (id, debugging) {
LCXL.initLEDs();
engine
.makeConnection('[Channel1]', 'track_loaded', function (value, group, controlName) {
LCXL.displayRelativeTempo(true);
})
.trigger();
engine
.makeConnection('[Channel2]', 'track_loaded', function (value, group, controlName) {
LCXL.displayRelativeTempo(true);
})
.trigger();
engine
.makeConnection('[Channel1]', 'play', function (value, group, controlName) {
let color = value == 0 ? LCXL.COLORS.AmberLow : LCXL.COLORS.Flashing(LCXL.COLORS.GreenHigh);
LCXL.sendSysex(LCXL.BUTTONS.Bottom1, color);
// TODO: ideally we want to call this method from one place only.
// But BMP of a loaded track is 0 until it gets played.
LCXL.displayRelativeTempo(false);
})
.trigger();
engine
.makeConnection('[Channel2]', 'play', function (value, group, controlName) {
let color = value == 0 ? LCXL.COLORS.AmberLow : LCXL.COLORS.Flashing(LCXL.COLORS.GreenHigh);
LCXL.sendSysex(LCXL.BUTTONS.Bottom8, color);
LCXL.displayRelativeTempo(false);
})
.trigger();
engine
.makeConnection('[Channel1]', 'cue_indicator', function (value, group, controlName) {
let color = value == 0 ? LCXL.COLORS.Orange : LCXL.COLORS.OrangeHigh;
LCXL.sendSysex(LCXL.BUTTONS.Top1, color);
})
.trigger();
engine
.makeConnection('[Channel2]', 'cue_indicator', function (value, group, controlName) {
let color = value == 0 ? LCXL.COLORS.Orange : LCXL.COLORS.OrangeHigh;
LCXL.sendSysex(LCXL.BUTTONS.Top8, color);
})
.trigger();
engine
.makeConnection('[Channel1]', 'pfl', function (value, group, controlName) {
let color = value == 0 ? LCXL.COLORS.RedLow : LCXL.COLORS.RedHigh;
LCXL.sendSysex(LCXL.BUTTONS.Top2, color);
})
.trigger();
engine
.makeConnection('[Channel2]', 'pfl', function (value, group, controlName) {
let color = value == 0 ? LCXL.COLORS.RedLow : LCXL.COLORS.RedHigh;
LCXL.sendSysex(LCXL.BUTTONS.Top7, color);
})
.trigger();
// Tempo sliders indicators
engine
.makeConnection('[Channel1]', 'rate', function (value, group, controlName) {
// Deck's absolute slider.
var color = LCXL.COLORS.GreenHigh;
if (value > 0) {
color = LCXL.COLORS.AmberLow;
} else if (value < 0) {
color = LCXL.COLORS.OrangeLow;
}
LCXL.sendSysex(LCXL.POTS.Low1, color);
// Deck's relative slider.
LCXL.displayRelativeTempo(false);
})
.trigger();
engine
.makeConnection('[Channel2]', 'rate', function (value, group, controlName) {
// Deck's absolute slider.
var color = LCXL.COLORS.GreenHigh;
if (value > 0) {
color = LCXL.COLORS.AmberLow;
} else if (value < 0) {
color = LCXL.COLORS.OrangeLow;
}
LCXL.sendSysex(LCXL.POTS.Low8, color);
// Deck's relative slider.
LCXL.displayRelativeTempo(false);
})
.trigger();
engine
.makeConnection('[Channel1]', 'loop_enabled', function (value, group, controlName) {
let color = value == 0 ? LCXL.COLORS.OrangeLow : LCXL.COLORS.Flashing(LCXL.COLORS.OrangeLow);
LCXL.sendSysex(LCXL.BUTTONS.Bottom3, color);
})
.trigger();
engine
.makeConnection('[Channel2]', 'loop_enabled', function (value, group, controlName) {
let color = value == 0 ? LCXL.COLORS.OrangeLow : LCXL.COLORS.Flashing(LCXL.COLORS.OrangeLow);
LCXL.sendSysex(LCXL.BUTTONS.Bottom6, color);
})
.trigger();
engine
.makeConnection('[Channel1]', 'PeakIndicator', function (value, group, controlName) {
let color = value == 1 ? LCXL.COLORS.RedHigh : LCXL.COLORS.GreenLow;
LCXL.sendSysex(LCXL.POTS.High4, color);
})
.trigger();
engine
.makeConnection('[Channel2]', 'PeakIndicator', function (value, group, controlName) {
let color = value == 1 ? LCXL.COLORS.RedHigh : LCXL.COLORS.GreenLow;
LCXL.sendSysex(LCXL.POTS.High5, color);
})
.trigger();
LCXL.TIMER = engine.beginTimer(500, LCXL.sendPeriodicInfo);
}
LCXL.shutdown = function () { };
LCXL.sendPeriodicInfo = function () {
// This has been moved to events. So no need for periodic gathering now. And it's even better because it sends only when the peak.
// let deck1GainColor = engine.getValue('[Master]', 'PeakIndicatorL') == 1 ? LCXL.COLORS.RedHigh : LCXL.COLORS.GreenLow;
// LCXL.sendSysex(LCXL.POTS.High4, deck1GainColor);
// let deck2GainColor = engine.getValue('[Channel2]', 'PeakIndicator') == 1 ? LCXL.COLORS.RedHigh : LCXL.COLORS.GreenLow;
// LCXL.sendSysex(LCXL.POTS.High5, deck2GainColor);
LCXL.displayPlayPosition(LCXL.POTS.Low2, engine.getValue('[Channel1]', 'playposition'));
LCXL.displayPlayPosition(LCXL.POTS.Low7, engine.getValue('[Channel2]', 'playposition'));
};
LCXL.initLEDs = function () {
midi.sendShortMsg(0xB0, 0x00, 0x00); // Reset device lights.
midi.sendShortMsg(0xB0, 0x00, 0x28); // Set automatic LED flashing.
LCXL.sendSysex(LCXL.BUTTONS.Up, LCXL.COLORS.RedLow);
LCXL.sendSysex(LCXL.BUTTONS.Down, LCXL.COLORS.RedLow);
LCXL.sendSysex(LCXL.BUTTONS.Left, LCXL.COLORS.RedLow);
LCXL.sendSysex(LCXL.BUTTONS.Right, LCXL.COLORS.RedLow);
LCXL.sendSysex(LCXL.BUTTONS.Top4, LCXL.COLORS.YellowLow);
LCXL.sendSysex(LCXL.BUTTONS.Top5, LCXL.COLORS.YellowLow);
LCXL.sendSysex(LCXL.BUTTONS.Bottom4, LCXL.COLORS.YellowLow);
LCXL.sendSysex(LCXL.BUTTONS.Bottom5, LCXL.COLORS.YellowLow);
LCXL.sendSysex(LCXL.BUTTONS.Bottom2, LCXL.COLORS.GreenLow);
LCXL.sendSysex(LCXL.BUTTONS.Bottom7, LCXL.COLORS.GreenLow);
// LCXL.sendSysex(LCXL.POTS.High2, LCXL.COLORS.AmberLow);
// LCXL.sendSysex(LCXL.POTS.Mid2, LCXL.COLORS.AmberLow);
// LCXL.sendSysex(LCXL.POTS.Low2, LCXL.COLORS.AmberLow);
// LCXL.sendSysex(LCXL.POTS.High7, LCXL.COLORS.AmberLow);
// LCXL.sendSysex(LCXL.POTS.Mid7, LCXL.COLORS.AmberLow);
// LCXL.sendSysex(LCXL.POTS.Low7, LCXL.COLORS.AmberLow);
};
LCXL.displayRelativeTempo = function (isLoaded) {
let deck1BPM = Math.round(engine.getValue('[Channel1]', 'bpm'));
let deck2BPM = Math.round(engine.getValue('[Channel2]', 'bpm'));
if (isLoaded) {
LCXL.sendSysex(LCXL.POTS.Mid1, LCXL.COLORS.Off);
LCXL.sendSysex(LCXL.POTS.Mid8, LCXL.COLORS.Off);
return;
}
var deck1RelativeColor;
var deck2RelativeColor;
if (deck1BPM < deck2BPM) {
deck1RelativeColor = LCXL.COLORS.OrangeHigh;
deck2RelativeColor = LCXL.COLORS.RedHigh;
} else if (deck1BPM === deck2BPM) {
deck1RelativeColor = LCXL.COLORS.GreenHigh;
deck2RelativeColor = LCXL.COLORS.GreenHigh;
} else {
deck1RelativeColor = LCXL.COLORS.RedHigh;
deck2RelativeColor = LCXL.COLORS.OrangeHigh;
}
LCXL.sendSysex(LCXL.POTS.Mid1, deck1RelativeColor);
LCXL.sendSysex(LCXL.POTS.Mid8, deck2RelativeColor);
};
LCXL.displayPlayPosition = function (ledNumber, position) {
var color = LCXL.COLORS.Off;
if (position == 0 || position == 1 || position == 0.5) {
color = LCXL.COLORS.Off;
} else if (position <= 0.16) {
color = LCXL.COLORS.GreenLow;
} else if (0.16 < position && position <= 0.32) {
color = LCXL.COLORS.Flashing(LCXL.COLORS.GreenLow);
} else if (0.32 < position && position <= 0.48) {
color = LCXL.COLORS.YellowLow;
} else if (0.48 < position && position <= 0.64) {
color = LCXL.COLORS.Flashing(LCXL.COLORS.YellowLow);
} else if (0.64 < position && position <= 0.90) {
color = LCXL.COLORS.RedLow;
} else {
color = LCXL.COLORS.Flashing(LCXL.COLORS.RedLow);
}
LCXL.sendSysex(ledNumber, color);
};
LCXL.sendSysex = function (control, color, isToggle) {
var templateID = 0x00;
var toggleButtonMarker = isToggle ? 0x7B : 0x78;
var start = [0xf0, 0x00, 0x20, 0x29, 0x02, 0x11, toggleButtonMarker, templateID];
var end = [0xf7];
var msg = start.concat([control, color]).concat(end);
midi.sendSysexMsg(msg, msg.length);
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment