This chapter requires QLab 4.1 or later.

If you are familiar with constructing TouchOSC controls on tablets (or phones) then you may have been frustrated with some of the limitations of that program. Lemur by liine is a more advanced type of programmable controller for tablets that, although more complex than TouchOSC, has some significant advantages:

  • Objects in Lemur can output OSC or MIDI only on certain “state changes,” so, for example, they can send a message when value X goes from 0 to a positive number, but not when value X goes from a positive number to 0. This avoids the double trigger problem in TouchOSC when a message is sent on both the press and the release of a button.
  • Lemur contains a scripting language, which means it can offer far more functionality than just simply sending a single OSC message associated with each on-screen control.
  • There is a greater range of objects including some which operate using physics modeling like bouncing or friction.
  • The editor app syncs with the tablet live, rather than requiring a manual refresh. All changes to the programming are reflected almost immediately on the tablet.

Lemur is also a real bargain. It started life in 2007 as a touchscreen hardware device called the Lemur Jazz Mutant which cost thousands of dollars. Nowadays it’s an app that runs on an iOS or Android device, providing even greater functionality than the original controller for around $25.

This example tutorial constructs a physics-based auto quad-panner to control the crosspoint matrix in an Audio cue in QLab. Here’s it is in action, best viewed full screen:

What’s Going On Here

Because the values shown in QLab’s crosspoints do not update continuously, the thing to watch are the level meters in the cue outputs. The video uses a sine wave as the target of the Audio cue so that you can see what’s happening as clearly as possible.

The four sliders on the left of the Lemur controller send OSC to QLab to control the levels of the first four crosspoints in row 1 of the Audio cue’s level matrix. The target here is a mono recording, so only row 1 is visible.

Below the faders are text fields showing a readout of each fader’s current level.

In the center of the screen is a Multiball object. This is a two dimensional control pad that contains a physics engine allowing up to 10 “balls” to bounce around within its bounds. The rules of the physics engine are adjustable.

Below that is a switch which enables the physics engine (“Motion” mode) or disables it (“Manual/LFO” mode.)

A fader across the top of the Multiball object controls the speed of the ball when in Motion mode.

In Motion mode, you can throw the ball by dragging and releasing it. It will bounce around the rectangle forever at the release velocity, which can be modified using the speed fader above.

In Manual/LFO mode, the ball can be dragged around the window and remains where it is when you lift your finger. In this mode, the speed fader adjusts the drag against the ball. Set it higher for faster response, or lower for smoother response.

If you tap inside the rectangle but not on the ball, the ball will move to where you’ve tapped.

When in Manual mode, the LFO (low frequency oscillator) can be switched on. This allows the ball to be driven by a sine wave generator, with variable frequencies for the x and y axis set by the two knobs on the right of the controller. This allows complex but regularly repeating pan patterns.

How It Works

While this tutorial produces a quad panner that is usable, the main purpose of this chapter is to introduce some of Lemur’s programming possibilities. If you are mathematically inclined, you will be able to improve many aspects of the project such as introducing pan laws, other LFO shapes, and so on.

Before starting this tutorial, we recommend a quick skim of the Lemur manual and/or a browse through the Lemur tutorial videos](https://liine.net/en/downloads/lemur).

The QLab Workspace

Here is the QLab workspace we are going to control:

QLab workspace

The audio cue has a mono file as its target which is sent to cue outputs 1 through 4. You can control the overall level in QLab using the sliders, but any changes you make to the crosspoints will be overwritten by Lemur sending OSC messages to control the crosspoints, which is how the quad panning will happen.

Connecting your Mac to Lemur

Open Lemur Editor on the Mac and Lemur on your iPad or Android tablet.

Lemur Editor

Open a new document in Lemur Editor and set the document size to iPad (or the size of your device) using the pop up menu in the top right of the editor window.

Click the play arrow button in the top right corner of the window to get a list of available devices running Lemur:


Select the iPad you want to sync with and click Connect. Then, click on the circular arrows “sync” button in the top right.

You are now synchronized.

Connecting Lemur to QLab

On your tablet, tap on the gear button in the top left corner to get to Settings:

Lemur settings

Under OSC Targets, which are analogous to QLab’s network patch destinations, connect OSC 0 to your Mac entering the Mac’s IP address into the Host field and 53000 (the port on which QLab listens for OSC) into the Port field. Then tap Done.

Building the faders

First, we will create four faders and set them up to send OSC messages to control the QLab crosspoints.

In Lemur Editor on the Mac, drag a fader object from the palette to the left of the Lemur work area.

Lemur fader

In the OSC Mapping panel at the top left of the editor, type a custom address into the address area for the x parameter:


Note that the pop-up menu to the right of the field is set to any which means this will be transmitted any time the value of x changes. Have a look at the other options offered on this menu, but make sure you set it back to any.

This OSC message sets the level of crosspoint [1,1] for the currently selected cue in QLab. If you want the messages to be sent to a specific cue number instead, you can change all references to selected throughout this chapter to the cue number you want to address.

QLab will want levels in the range -60 to 0 (by default), so we type these into the custom scale fields.

All other parameters can be left as the default.

When you finish, the OSC mapping pane should look like this:

OSC mapping

Click anywhere outside the panel to commit the changes.

We can now test this by moving the fader and checking the crosspoint in QLab. QLab only shows the updated value once you stop changing it, so drag the fader and then let go.

We can now copy and paste this fader object three times to make the four faders we need. We also need to edit the custom address fields for each slider so that they each control the correct crosspoint.

Four faders

  • Fader number 2 → /cue/selected/level/1/2
  • Fader number 3 → /cue/selected/level/1/3
  • Fader number 4 → /cue/selected/level/1/4

Again, check your work by adjusting the faders one at a time, and looking at the cue in QLab to confirm the change.

One all four faders are working, we can label them. Monitor objects can be used to both label the faders and display their values.

Lemur Fader objects produce an OSC output between 0.0 and 1.0, which is why we adjusted the Scale boxes to re-scale that output to a range of -60 to 0. We’ll want the Monitor object to display values scaled to this range as well.

Drag a Monitor object from the palette and place it beneath Fader 1. In the Properties panel in the lower left of the editor, name it slider1.

Set the width to 50, the height to 32, and the font size to 9pt.

Type Fader1.x in the value field to tell it what to display, and set the precision to 1 decimal place.

Monitor properties

It’s probably worth pointing out at this stage that all names in Lemur are case sensitive. If you type “fader.x” your value will be in red which means it is invalid, and you’d need to work out for yourself that the reason is the lower-case “f” in “fader.x”. Typing “Fader.x” will correct it and the text in the field will turn black indicating a valid value.)

If you move the fader up and down, a value between 0.0 and 1.0 will be displayed.

To get the actual value sent to the QLab crosspoint we change the value to


which scales the displayed value to a floating point number (float) between -60.0 and 0.0.

Copy and paste the Monitor object three times to get four Monitors, place them beneath each fader. Lemur Editor will automatically name them slider2, slider3, and slider4.

Edit the value of slider2 to range(Fader2.x,-60,0), and so on for sliders 3 and 4.

Four faders

Building the X/Y pad in Lemur

The Multiball object in Lemur is a controller object on steroids! It can have up to ten cursors, represented by balls. These balls have a full physics engine, so their speed, friction, and attraction to the operator’s fingers can all be controlled either directly or through mathematical expressions.

This example just uses a single ball to control four faders for a mono source but is easily expanded to, for instance, four balls to control each track of a four-track wav, by simultaneously sending OSC to sixteen crosspoints.

Drag the Multiball object to the center of the editor workspace.


In the Properties panel, set the number of balls to 1. This creates a ball numbered 0. If we add more balls, it might be confusing to have a ball numbered 0 so we are going to change the ball numbering. To do this, increase the number of balls to 2.

Two balls

We then highlight the project folder in the Project pane in the bottom right of Lemur Editor, and click the Script button at the bottom of the pane.

The Script button

Create a new script called “setup”.

Setup script

In the Script pane at the bottom center of the window, click on the Execution drop-down menu and choose “On Load”. Then, type the following into the script field:



Note that each script line is terminated with a semicolon (;).

This instruction tells Lemur to only enable the second ball, i.e the one labeled 1, and to hide the ball labeled 0.

We now need to link the ball to the four faders.

Highlight the Multiball object in the Project pane and click the script button. Make a new script with the name faderDriver which will be nested under the Multiball object. Select the script in the Project window, and set the execution to frame, which means the script will execute 60 times per second. Then, type the following in the script window:


Multiball script

(Note that x[1] in the script means the x-position of ball 1.)

Fader 1, which will control the front left output of QLab, is set to a value which decreases as the ball moves away from the top left of the Multiball object. Remember, the raw output of the object for each axis (x and y) is a value between 0.0 and 1.0. As the ball moves left to right, the x value increases. Subtracting the x value from 1 gives the decreasing value we need. We multiply the x value by the y value. When the ball is at the top we are multiplying by 1 when it is in the middle by .5, and at the bottom by 0, For faders 1 and 2 these are the values we want so we use the y value as is. For the rear outputs (faders 3 and 4) we want the values to increase as the y value decreases so again we subtract the y value from 1.

Remember to click outside the script window to commit the edit.

You can now test that the movement of the ball in the Multiball object is acting on the faders.

Move the ball to each of the corners and you will see that the fader for that corner of the quad pan will be at 0dB, and all others will be at -60dB.

If you move the ball to the center top of the controller window you will see that faders 1 and 2 are both at 30dB. Even allowing for the fact that the sound is coming out of two speakers at that level instead of just one, this will create a significant dip.

If you move the ball to the center of the left-hand side of the object you will see the same level dip between faders 1 and 3. With the ball in the center of the object, the sound comes from all 4 speakers but at a level of -44dB; again a substantial dip.

What we need to do is transform the linear output of the Multiball object to produce a logarithmic relationship with the fader, so that when the ball is between 2 outputs the dip is only around 6dB and when it is in the center the dip is around 12dB. This will result in a reasonably constant output regardless of the position of the ball.

For fader 1 a formula that achieves the above is:


(Thanks to Rich Walsh for help with this math.)

Replace the contents of the “faderDriver” script in the Multiball object with this:

decl panlawval;

We have replaced the value 6.4 with a variable panlawval which is declared at the top of the script. Changing the value of this variable will allow fine control of the pan law applied to the controller, without having to type in that new value four times.

Return to the Multiball object and drag the ball around, and you will see that the dip at the center of each edge and in the center of the object is reduced to the level required.

Adding the physics engine

First, drag a Fader object from the palette to the controller. Resize it to be horizontal, and place it above the Multiball object. Name it “speed”.

Drag a Monitor object from the palette to a position above the Fader and name it “SPEED”.

In the Script panel, define what SPEED will monitor by typing:


This displays the value from the “speed” fader with its range scaled between 0.0 and 5.0.

Set the precision of the Monitor object to 2 in the Properties panel.


Next, drag a CustomButton object from the palette to the controller. Name it “Motion” and set its behavior to Switch.

In the Properties panel, next to Style Off, enter “Manual/LFO”. Next to Style On, enter “MOTION”.

In the Project panel, select the Multiball object and create two scripts called “switchOFF” and “switchON”.

In the Script panel for the “switchOFF” script, set Execution to On Expression x and set the trigger to “message sent when value goes from positive to 0.” Then enter this text:

setattribute (Multiball,'physic',1);

This sets the physics mode of the Multiball object to “interpolate”.

For the “switchON” script, set Execution to On Expression x and set the trigger to “message sent when value goes from 0 to positive.” Then enter this text:

setattribute (Multiball,'physic',2);

This sets the physics mode of the Multiball object to “Mass-Spring”.

Physics script

We then need to set the physics variables in the Behavior panel.

Set cursor mode to Barycentric.

Set attraction to (1*range(speed.x,0,5))+Motion.x This sets the speed the ball will travel to a finger placed on the pad.

Set Friction to 0+z. This means that in Motion mode, the ball will have a friction of 0 until a finger is placed on the object. When this happens, Multiball.z will change from 0 to 1, which will be added to the friction. IN Lemur, 1 is the highest possible value, so 1 means “full friction”, causing the ball to stop.

Finally set the speed behavior to this:


If the Motion switch is OFF (Manual/LFO Mode), its x value is 0 so the speed will be 0: (value of speed fader) multiplied by 0 equals 0. If the Motion Switch is ON, its x will be 1, so the speed will be (value of speed fader) multiplied by 1.


Test the physics engine by setting the speed fader to 1, switching MOTION on, and throwing the ball by dragging and releasing it. Here are a few things to try:

  • Increase and decrease the speed.
  • Stop the ball by placing a finger on it.
  • Switch to Manual/LFO Mode.
  • Set the speed to maximum.
  • Drag the ball around.
  • Set the speed to 0.03 and place a finger some distance from the ball.

Creating the LFO

Drag a CustomButton object from the palette to the controller. Name it “LFOswitch”, and set its behavior to Switch.

In the Properties panel, label its off state as “LFO OFF” and its on state as “LFO ON”.

Drag two Knob objects in from the palette and name them “LFOx” and “LFOy”.

Drag two Monitor objects in, and name them “LFO_X” and “LFO_Y”.

“LFO_X” needs this entered in the Script panel:


“LFO_y” needs:


In the Project panel, select Project and create a new script by clicking the Script button. Name the script “LFO”, set the execution to On Frame, and type this in the script window:

  Multiball.x[1]=sin(time*(range(LFOx.x,-1,1)*range(speed.x,0,5)))*0.5+ 0.5;
  Multiball.y[1]=sin(time*(range(LFOy.x,-1,1)*range(speed.x,0,5)))*0.5+ 0.5;

This means if the expression in the parentheses is true (in this case, if LFOswitch.x is equal to 1, i.e is ON), then the script between the braces will be executed. Note that “is equal to” is expressed by a double equals sign.

This script is a fairly conventional sine wave generator.

Set the speed controller somewhere in the middle and set two values on the LFO knobs. Switch the LFO on, and experiment with different values of Speed and Knob controls.

Select the Motion “switchON” script in the Project panel and add the following line to the script:


This will switch LFO off whenever Motion is switched on.

Finally, we can slightly reduce the data rate of the OSC messages. Lemur is very good at filtering unnecessary messages and will only send them when a value changes. In our case, this means when the fader levels change. When the ball is moving, the sliders are continuously updated and send OSC messages to QLab once per frame, which is 60 times per second or about once every 16ms.

We can probably reduce this to a quarter this rate without causing any audible artifacts. To do this we will only execute the Multiball faderDriver script every 64ms.

We do this by starting one of Lemur’s ten internal clocks and setting it’s tempo to 60bpm. Then, we can set the execution of the “faderDriver” script to trigger from this clock rather than triggering on every frame.

Select the “setup” script in the Project panel and add the following lines to the existing script:

clock_start (0);
clock_setbpm (0,60);

Then, select the Multiball “faderDriver” script in the Project panel and set the execution to “On Clock”. Then set the two following drop-down menus to “Clock 0” and “On 1/64 note”.

Rate limit

A quarter note at 60bpm results in one trigger per second, so a 1/64 note at 60bpm results in sixteen triggers per second which should be fine for our needs. This cuts the flow of OSC messages to one quarter of their previous rate.