Epic Random Balls

This chapter looks at a whole cornucopia of useful advanced programming techniques. It is based around a workspace that obtains six numbers, using a variety of methods, and displays them in a video and audio sequence as numbered balls rolling onto the screen, being sorted into numerical order, and then rolling off again.

The example workspace can be adapted for all sorts of uses; escape room puzzles, magic tricks, games requiring random numbers, lottery scenes in plays and other dramatic works of fiction, and more, but the main purpose of the chapter is to look at how a very complex workspace can be automated and presented to an operator in a clear way that minimizes the possibility of any errors in its operation.

One thing it is definitely not is a workspace for generating and presenting actual lottery draws, or any other form of gambling. It lacks the auditing and security features necessary to do this, and lotteries are subject to very tight regulation in most legal jurisdictions and are completely illegal and subject to serious penalties in others.

The workspace generates pseudo-random numbers by using AppleScript and by using a Start Random Group cue. For truly random numbers, it utilizes a third-party server which derives its number sequences from multiple radio tuners tuned to pick up RF noise in the atmosphere.

You can also input numbers manually without the use of dialog boxes if, for instance, you have audience members call the numbers out as part of a trick, or are just displaying the output of a physical ball selection device.

In addition to the choice of random generators, the workspace has many other options available: padding of single digit numbers with leading zeroes, sound on or off, confirmation of manual input before the ball is rolled, fully automatic cueing of the entire sequence, or splitting it into shorter sections with manual cues for each section. These preferences are all set using a single cue cart, which eliminates the need for multiple dialogs when configuring the workspace operation.

The workspace also demonstrates a method of creating graphic elements on the fly, and then sorting them and changing their positions based on their content.

The operator’s cue list is reduced to the bare minimum of necessary cues; the arming and disarming of these cues are controlled to minimize accidental operation, and information about the current option preferences and required operator input is provided.

The examples in this chapter are shown in QLab 5, but all the scripts are functionally identical in QLab 4. They only need any reference to application id "com.figure53.QLab.5" changed to application id "com.figure53.QLab.4"

Here it is in action, configured for manual number input and automatic cueing, followed by true random number generation and manual cueing:

At the beginning of the video, the “Preferences” cart is configured to:

  • Not pad single digits with a 0,
  • Use manual number entry without validation,
  • Use autopilot, to run everything automatically after the six numbers have been entered.

The first Group cue in the main cue list automatically expands itself when triggered to show confirmation of the preference settings.

Cue “PT0” is started, which begins the music and initializes the Workspace.

The name of “PT1” is changed to indicate that number entry can begin.

Each ball number is entered, followed by . and the balls are created; an alert gives the operator a chance to cancel a mistyped entry, and after confirmation, the ball rolls onto the screen.

After the first four balls, the Validate Toggle in the cue cart is pressed to switch it off. This changes the entry sequence so that pressing . now rolls the ball immediately. Balls can also be cancelled after number entry by keying * as shown on the first attempt at numbering the sixth ball.

The input routine detects out-of-range and duplicate ball numbers and alerts the operator when these occur.

After the sixth ball, the name of cue “PT1” is changed to indicate the sequence will now auto-run to the end.

The balls float apart and are sorted in numerical order on screen, the ordered sequence is announced, and the balls roll off the screen.

For the second run, the preferences are changed in the cart to pad single-digit numbers with zeros, to get six true random numbers from a remote server, and to require each section of the sequence to be started with a GO.

Cue “PT0” is run to initialize, the music restarts and the name of cue “PT1” changes to indicate the online interaction to obtain the numbers; it then changes to indicate that sequences are to be cued manually and cue “PT2” is armed to allow the operator to roll the balls on screen.

Further GOs trigger the other parts of the sequence when required.

How It Works

Although the workspace presents a very short cue list to the operator, there are another 250 cues in another list that are doing all the work, including 24 Script cues, 77 Network cues, and 32 Fade cues.

The cues in the preferences cart primarily change the colors of each section of cart cues, and also updates the “INFO” cues in the operator’s cue list to indicate the current preferences in use.

The preferences cart

The cues in the preferences cart use their cue numbers in combination with their cue names to indicate their function without extraneous numbers. A bit of imagination has to be used to ensure the button legends make sense while also making sure that the cue numbers are unique!

For example, the cue numbered “NO” and named “ZERO”, which appears as “NO • ZERO”, is a Script cue containing this script:

tell application id "com.figure53.QLab.5" to tell front workspace
	set q color of cue "NO" to "green"
	set q color of cue "PAD" to "blue"
	set q name of cue "INFO1" to "SINGLE DIGIT  BALL NUMBERS  HAVE NO PADDING"
end tell

The cue numbered “PAD” and named “ZERO”, which appears as “PAD • ZERO” contains this script:

tell application id "com.figure53.QLab.5" to tell front workspace
	set q color of cue "PAD" to "green"
	set q color of cue "NO" to "blue"
	set q name of cue "INFO1" to "SINGLE DIGIT  BALL NUMBERS HAVE LEADING ZERO"
end tell

When other scripts need to know which preference is set, they just test the q color of the relevant button:

if the q color of cue "NO" is "green" then
			set the live text of theCue to (theNumber as integer)
		else
			set the live text of theCue to theNumber
		end if
end tell

The “ON • SOUND” and “OFF • SOUND” cues are similar but also arm and disarm all Audio cues in the entire workspace.

ON:

tell application id "com.figure53.QLab.5" to tell front workspace
	set theSoundCues to (cues whose q type is "audio") as list
	repeat with eachCue in theSoundCues
		set the armed of eachCue to true
	end repeat
	set q color of cue "ON" to "green"
	set q color of cue "OFF" to "purple"
	set q name of cue "INFO2" to "ALL SOUND IS ON"
end tell

OFF:

tell application id "com.figure53.QLab.5" to tell front workspace
	set theSoundCues to (cues whose q type is "audio") as list
	repeat with eachCue in theSoundCues
		set the armed of eachCue to false
	end repeat
	set q color of cue "OFF" to "green"
	set q color of cue "ON" to "purple"
	set q name of cue "INFO2" to "ALL SOUND IS OFF"
end tell

There are four cart cues which select the number method used. They form a radio group (a group of buttons in which only one can be “on” at a time) with a background color of orange. The selected cue is colored green.

Here’s the script for the cue “QL • QLAB RANDOM GROUP”:

tell application id "com.figure53.QLab.5" to tell front workspace
	set q color of cue "QL" to "green"
	set q color of cue "AS" to "orange"
	set q color of cue "MAN" to "orange"
	set q color of cue "ORG" to "orange"
	set the q name of cue "INFO3" to "RANDOM NUMBER SEQUENCE GENERATED BY QLAB RANDOM GROUP"
end tell

The other cues in the radio group are similar, only changing which cue is green, and the information they provide for the operator’s list. The script for cue “MAN • NUMBER ENTRY” is slightly more complicated:

tell application id "com.figure53.QLab.5" to tell front workspace
	set q color of cue "MAN" to "green"
	set q color of cue "AS" to "orange"
	set q color of cue "QL" to "orange"
	set q color of cue "ORG" to "orange"
	if q color of cue "VALIDATE" is "green" then
		set the q name of cue "INFO3" to "MANUAL NUMBER ENTRY WITH CONFIRM BEFORE BALL ROLL"
	else
		set the q name of cue "INFO3" to "MANUAL NUMBER ENTRY WITH NO CONFIRMATION BEFORE BALL ROLL"
	end if
end tell

The cue name of cue “INFO 3” is dependent on the color of the “VALIDATE • TOGGLE” cue. This cue is just scripted to change its own color as a toggle, instead of using two separate buttons, and refers to the color of cue “MAN” to include its status in the name of cue “INFO 3” if required:

tell application id "com.figure53.QLab.5" to tell front workspace
	if q color of cue "VALIDATE" is "green" then
		set q color of cue "VALIDATE" to "red"
		if q color of cue "MAN" is "green" then set the q name of cue "INFO3" to "MANUAL NUMBER ENTRY WITH NO CONFIRMATION BEFORE BALL ROLL"
	else
		set q color of cue "VALIDATE" to "green"
		if q color of cue "MAN" is "green" then set the q name of cue "INFO3" to "MANUAL NUMBER ENTRY WITH CONFIRM BEFORE BALL ROLL"
	end if
end tell

The rest of the cues are in the main cue list:

Cue “PT0” in the operator’s list starts cue “INIT”

The INIT cue

The cues in the Timeline Group:

  • Stop the music if it is still running.
  • Switch Live Fade Preview off. This is vitally important, as we will see later that the parameters of Fade cues are calculated and set during the operation of the sequence. If Live Fade Preview were active, then these fades would jump their targets to new values before the Fade cues were run.
  • Set the playhead of the “Variables” cue list to cue “sequence” so we can see the variables changing.
  • Reset the Group cue numbered “RAND”, which is a Start Random Group cue with a cue for every possible ball number within it. Start Random Groups use a round-robin triggering method and only repeat a cue after all cues have been played; this means we can’t produce balls with duplicate numbers unless one round-robin cycle has ended and another has started. For example, ball 33 could be the penultimate random number of the first round robin cycle and the second number of the second cycle, which could result in a duplicate in a sequence of six numbers. To ensure this can never happen, the group is reset every time the cue “INIT” is run by a Reset cue targeting cue “RAND”,

Because there are many scripts that may need to share items of information, we need to store this data in the notes and other parameters of cues. In this workspace, all the cues that store variables this way are contained in a single cue list named “Variables”:

The variables cue list

The current ball number is stored as a number in the pre-wait of a cue numbered “ballNo” and is set to an initial value of 1.

The cue numbered “rand” stores randomly generated numbers in its cue name.

The name of cue “number” is used for building manually entered numbers from keyboard entry. It is initialized to an empty string "".

The notes filed of cue “sequence”, also initially empty, holds the sequence of six numbers as it is being built, and the notes of cue “sorted” holds that same sequence sorted into numerical order.

All cues of the operator’s cue list are armed, and then the cues which we don’t want the operator to be able to run (until they are required) are disarmed.

A Script cue interrogates the colors in the preferences cart to determine if the ball numbers are going to be manually entered or use a randomly generated sequence and sets the info in the operator’s list accordingly. If the numbers are to be automatically generated, then cue “AUTO” is started.

If the cue numbers are to be manually generated, then the operator’s cue list will look like this:

Manual entry

This reminds the operator of the method for ball number entry.

The numbers 0 through 9, either on the main keyboard or the number pad, are used to enter the digits. If * is keyed, that number entry is cancelled. The period (.) on the keyboard, or decimal (.) on the number pad (in regions that use a period as a decimal separator) act as the enter key; this will either result in the ball being immediately rolled, or in an alert notifying the operator of the number entered so that it can be cancelled or confirmed and rolled as required, depending on the preference set by the “VALIDATE” cue in the preferences cart. You can change these hotkeys on the relevant cues in the main cue list to accommodate other keyboard layouts and assign MIDI triggers if required.

If an out-of-range number is encountered, a dialog will warn the operator.

When a valid key is pressed, it triggers a Network cue with an OSC message, e.g.

NUM1

Cue NUM1 sends this fiendishly clever OSC message:

/cue/NUM/notes ”#/cue/NUM/notes#1”

It’s generally true that you can’t perform calculations or other operations in OSC queries; however, there are a few exceptions and string concatenation (which is the operation of joining character strings together end-to-end) is one of them. This OSC message tells QLab to store the current text of the notes of cue “NUM” plus the character “1” in the notes of cue “NUM”. So if we typed 1, the notes would be 1. If we typed 1 again, the notes would be 11.

Here are the cues that handle numeric entries.

Number entry cues

When the period or decimal key is pressed (.), it triggers a Script cue to validate the number and generate the ball image by adding the number to the ball graphic. The script verifies that the number is in range and that it is not a duplicate; if the number is a single digit, it pads it with a leading zero. Valid formatted numbers are then stored in the notes of cue “sequence” as paragraphs to build the sequence of six numbers.

tell application id "com.figure53.QLab.5" to tell front workspace
	set theTopLimit to 49
	set theBottomLimit to 1
	set theCue to cue ("DIG" & ((pre wait of cue "ballNo") as integer as text))
	set theNumber to (q list name of cue "number" as text as integer)
	set theBall to pre wait of cue "ballNo" as integer
	set theLength to (count of characters of (theNumber as text))
	if ((theNumber > theTopLimit) or (theNumber < theBottomLimit)) then
		set q name of cue "number" to ""
		display alert "Number out of Range" giving up after 3
		return
	end if
	if theLength is 1 then set theNumber to "0" & theNumber
	if the notes of cue "sequence" contains (theNumber as text) then
		set q name of cue "number" to ""
		display alert "Duplicate Number" giving up after 3
		return
	end if
	if q color of cue "VALIDATE" is "green" then display dialog "Ball " & (theBall as text as integer) & "..... " & theNumber buttons {"Cancel", "Roll"} default button "Roll"
	if the q color of cue "NO" is "green" then
		set the live text of theCue to (theNumber as integer)
	else
		set the live text of theCue to theNumber
	end if
	
	start cue ("BALL" & (theBall as integer as text))
	set the pre wait of cue "ballNo" to ((pre wait of cue "ballNo") + 1)
	set the q name of cue "number" to ""
	set the notes of cue "sequence" to notes of cue "sequence" & theNumber & return
end tell

This script builds the graphic for each ball by sending the entered number to a Text cue which will be superimposed on the ball graphic and then starts that Text cue and the Video cue displaying the ball. In the example, there are groups of cues for each ball numbered “BALL1” to “BALL6”, and each group contains one matching Text cue numbered “DIG1” to “DIG6”. The script looks to see if single digit numbers should be displayed as padded or not by referring to the q color of Cue “NO” in the preferences cart.

Here are the ball cues.

Ball cues

Each “BALL” Timeline Group contains a copy of the blank ball graphic, a Text cue that will be superimposed on top of the ball, another Timeline Group containing Audio cues that provide a sting to accompany each ball’s appearance, and a Script cue which starts an Audio cue announcing the ball’s number. Here’s that script:

tell application id "com.figure53.QLab.5" to tell front workspace
	set theQ to (1100 + (live text of cue "DIG1" as integer)) as text
	start cue theQ
end tell

The Audio cues with the spoken numbers are numbered from 1101, so the script works out which one to play by adding 1100 to the numerical value of the live Text of the Text Cue for that Ball.

Ball announcements

If the numbers are to be automatically generated, then cue “AUTO” is started. This is a Script cue with the this script:

set theTopLimit to 49
set theBottomLimit to 1
--fill notes of  cue sequence with 6 values separated by retuns by one of the 3 methods
tell application id "com.figure53.QLab.5" to tell front workspace
	set theDuplicateFinder to {}
	set theSeq to ""
	delay 1
	if the q color of cue "AS" is "green" then
		repeat until the (count of paragraphs of theSeq) > 6
			set theNumber to (random number from theBottomLimit to theTopLimit) as integer as text
			if theDuplicateFinder does not contain theNumber then set the end of theDuplicateFinder to theNumber
			set theSeq to theSeq & theNumber & return
		end repeat
		set the notes of cue "sequence" to theSeq
		
	else if the q color of cue "QL" is "green" then
		
		set the notes of cue "sequence" to ""
		reset cue "RAND"
		delay 0.1
		repeat 6 times
			start cue "RAND"
			delay 0.2
			set theNumber to q list name of cue "rand" as integer as text
			set theSeq to theSeq & (theNumber & return)
		end repeat
		set the notes of cue "sequence" to theSeq
		
	else if the q color of cue "ORG" is "green" then
		try
			set theMessage to "https://www.random.org/sequences/?min=" & (theBottomLimit as text) & "&max=" & (theTopLimit as text) & "&col=1&format=plain&rnd=new"
			set theNumbers to (do shell script "curl  " & quoted form of theMessage)
			repeat with eachParagraph from 1 to 6
				set theSeq to theSeq & (paragraph eachParagraph of theNumbers) & return
			end repeat
			set notes of cue "sequence" to theSeq
		on error
			display alert " Random.Org Server is not available set preferences to alternative random generator" giving up after 5
			start cue "CLEAR"
			return
		end try
	end if
	
	set theSeq to ""
	
	repeat with eachParagraph from 1 to 6
		set theParagraph to paragraph eachParagraph of (notes of cue "sequence" as text)
		set theLength to count of characters of theParagraph
		set theNumber to theParagraph as text as integer
		if theLength is 1 then
			set theSeq to theSeq & "0" & theParagraph & return
		else
			set theSeq to theSeq & theParagraph & return
		end if
	end repeat
	set the notes of cue "sequence" to theSeq
	set the playhead of first cue list whose q name is "Variables" to cue "refresh"
	set the playhead of first cue list whose q name is "Variables" to cue "sequence"
	
	
	if the q color of cue "AUTOPILOT" is "green" then
		delay 2
		start cue "ROLL"
	else
		set the playhead of (first cue list whose q name is "Operators List") to cue "PT2"
		set the armed of cue "PT2" to true
		set the q name of cue "PT1" to "Cue Sequences Manually"
	end if
	
end tell

This script first determines which random method is selected in the preferences cart, by referring to the q colors of cues “AS”, “QL”, and “ORG”.

If cue “AS” is green, the six random numbers will be generated using a simple AppleScript which gets the numbers by asking for a random number from theBottomLimit to theTopLimit, six times. As repeated values are possible, these are tested for and discarded until we have a sequence of six unique numbers. The numbers generated by AppleScript’s random function are only pseudo-random because they are based on a seed and a formula. If you don’t specify the seed you will get a random seed as well, and this generates numbers which are random enough for most needs but the numbers still won’t technically be truly random.

If cue “QL” is green, the six random numbers will be generated by starting a Start Random Group cue numbered “RAND” six times. Inside this Group cue is one Network cue for each possible ball number with the corresponding OSC message. That is to say, the Network cue for number 1 has the OSC message /cue/rand/name 1, and the cue for number 49 has the OSC message /cue/rand/name 49. The randomness of QLab’s Start Random group utilizes macOS’s arc4random function which is a considerable improvement on AppleScript’s random number generator. In all but the most rigorous, academic sense, this randomness can be considered truly random. But since this is the QLab Cookbook, and part of the goal of the Cookbook is to explore the outer edges of cueing possibilities, we can go deeper:

If cue “ORG” is green then you will get truly random numbers generated by atmospheric radio static. To obtain them, we use a free service available online at random.org.

random.org was created by Mads Haahr at Trinity College in Dublin in 1998. It now operates as a commercial service operated by Random and Integrity Service, Ltd. to provide audited and certifiable random numbers, but also provides free legacy tools that can be accessed openly (but subject to quotas) by just using a standard HTTP GET request like this one:

https://www.random.org/integers/?num=10&min=1&max=6&col=1&base=10&format=plain&rnd=new

This request returns ten numbers between 1 and 6, arranged in a single column, as plain text. rnd=new provides a truly random sequence.

Once you have the six numbers generated by one of these methods they are stored in the notes of cue “sequence”, one number per paragraph.

The script then uses the q color of cue “AUTOPILOT” in the cart to either start the next phase of the process by starting cue “ROLL” directly, or by arming cue “PT2” in the operator’s cue list and selecting it ready for the operator to use it to start cue “ROLL” manually. Cue “ROLL” is a Script cue with this script:

set theDelay to 4 --time between balls
tell application id "com.figure53.QLab.5" to tell front workspace
	set the armed of cue "PT2" to false
	repeat with eachBall from 1 to 6
		delay 0.5
		set theBall to (pre wait of cue "ballNo" as integer)
		set theCue to cue ("DIG" & theBall)
		
		if the q color of cue "NO" is "green" then
			set theNumber to (paragraph eachBall of (notes of cue "sequence" as text)) as integer as text
		else
			set theNumber to (paragraph eachBall of (notes of cue "sequence" as text)) as text
		end if
		
		set the live text of theCue to (theNumber)
		delay theDelay
		start cue ("BALL" & theBall)
		set the pre wait of cue "ballNo" to (theBall + 1)
	end repeat
end tell

This reads the number for each ball from the data stored in the notes of cue “sequence” and then processes these numbers in exactly the same way as if we were entering them manually.

The Video cue for the ball and its corresponding Text cue start off to the right of the screen, just out of sight.

Ball cue starting position

The Video cue for the ball has an associated Fade cue in its Timeline Group which fades the ball’s geometry, rotating it to make it appear it is rolling to its new x position. For Ball 1, we need to roll the ball all the way to the left-hand side of the screen.

Ball 1 ending position

The x translation becomes progressively less with each ball; ball 6 will end only a short distance from its starting position:

Ball 6 ending position

Regardless of whether the numbers are entered manually or generated automatically, the last ball’s Timeline Group also contains two Script cues.

The first is cue “SORT”:

tell application id "com.figure53.QLab.5" to tell front workspace
	set theBallsUp to {(paragraph 1 of (notes of cue "sequence" as text)), (paragraph 2 of (notes of cue "sequence" as text)), (paragraph 3 of (notes of cue "sequence" as text)), (paragraph 4 of (notes of cue "sequence" as text)), (paragraph 5 of (notes of cue "sequence" as text)), (paragraph 6 of (notes of cue "sequence" as text))}
	
	set theSortedList to my simple_sort(theBallsUp)
	set the notes of cue "sorted" to ""
	repeat with eachItem from 1 to 6
		set the notes of cue "sorted" to (notes of cue "sorted" & item eachItem of (theSortedList) & return) as text
	end repeat
	set the playhead of first cue list whose q name is "Variables" to cue "refresh"
	set the playhead of first cue list whose q name is "Variables" to cue "sorted"
	
	
	repeat with i from 1 to 6
		repeat with n from 1 to 6
			if (item i of theBallsUp) is item n of theSortedList then set theOffset to n
		end repeat
		set themovement to ((theOffset - i) * 300)
		set the translation x of cue ("SORT" & (i as text)) to themovement
		
	end repeat
	delay 3
end tell

on simple_sort(my_list)
	set the index_list to {}
	set the sorted_list to {}
	repeat (the number of items in my_list) times
		set the low_item to ""
		repeat with i from 1 to (number of items in my_list)
			if i is not in the index_list then
				set this_item to item i of my_list as text
				if the low_item is "" then
					set the low_item to this_item
					set the low_item_index to i
				else if this_item comes before the low_item then
					set the low_item to this_item
					set the low_item_index to i
				end if
			end if
		end repeat
		set the end of sorted_list to the low_item
		set the end of the index_list to the low_item_index
	end repeat
	return the sorted_list
end simple_sort

As we are only sorting six numbers, this uses a very basic sort algorithm (sourced from macosxautomation.com).

The real magic happens in the shortest part of the script:

repeat with i from 1 to 6
		repeat with n from 1 to 6
			if (item i of theBallsUp) is item n of theSortedList then set theOffset to n
		end repeat
		set themovement to ((theOffset - i) * 300)
		set the translation x of cue ("SORT" & (i as text)) to themovement
end repeat

These two nested repeat loops find the position that each ball needs to move to on screen in order to present all the balls in ascending numerical order, and set the required offset in the Fade cue for each ball that is from “SORT1” to “SORT6”

The sorted numbers are stored in the notes of cue “sorted” for use later.

The second Script cue has this script:

tell application id "com.figure53.QLab.5" to tell front workspace
	if q color of cue "AUTOPILOT" is "green" then
		delay 2
		start cue "DISPLAY"
		set q name of cue "PT1" to "NOW AUTORUNNING TO END OF SEQUENCE"
	else
		set q name of cue "PT1" to "CUE EACH PART OF SEQUENCE MANUALLY"
		set the playhead of (first cue list whose q name is "Operators List") to cue "PT3"
		set the armed of cue "PT3" to true
	end if
end tell

It reads the color of cue “AUTOPILOT” and either starts cue “DISPLAY” automatically or arms the operator’s starting cue and sets the playhead so it can be manually cued.

Display sorted balls

“DISPLAY SORTED BALLS” consists of three nested Timeline Groups each containing a Fade cue for each ball.

The first Timeline Group, “Split Balls”, splits the balls by fading the y translation of each ball so they form a diagonal line across the screen.

This is followed by the “Sort Balls” Timeline Group, which fades the x translation of each ball using a relative fade so that all the balls are in numerical order, by using the x translation values that were automatically set by the script in cue “SORT”

Finally, the “Return Balls” Timeline Group fades each ball’s y translation to return the balls back to a straight line.

The ball numbers are announced in numerical order using the “announce sorted balls” script in cue “ANN” (again started automatically or manually depending on the color of cue “AUTOPILOT”).

Then cue “CLEAR” rolls all the balls off to the right using six Fade cues in a Timeline Group to fade the x translation and z rotation of each ball.

The music fades and the playhead is reset, ready to run a new sequence if required.

Deserted fairground image “AI” generated in Adobe Photoshop.

Music: Epic Orchestra Loop by joshuaempyre, distributed under a Creative Commons CC4.0 Attribution License.