VPX Scripting — Part 3 (Cut & Paste)

Pat Hand backglass.
Backglass for Pat Hand by the user Hauntfreaks.

19 Apr 2024

“Hear me now and believe me later”


In the first part of this series we merely looked at the script, in the second part we did in fact get in and make changes to the script but since we were linting, if we did it right the changes made no difference to how the table played.

In this post we are going to do some significant script refactoring. Get your surgical gloves on.


Read the descriptions for many of the VPX tables posted online and you will at some point come across a reference to Fleep sounds. What is that?

My best guess is that “Fleep” is a username. But more to the point, the pinball tables that indicate they have Fleep sounds are talking about a collection of audio files that are generally regarded as very high quality as well as code in the table’s script that takes advantage of the positional audio feature of VPX (pan sounds: left and right; fade sounds: front and back). In short the pinball tables have more dynamic audio, better pinball sounds.

As an example of the Fleep sounds themselves: you know the “drain” is where your ball goes when it slips between your flippers — Fleep sounds have twelve different sounds for the ball going into the drain. They’re all similar of course but that random and subtle variation adds to the realism of the table.

I started my journey modifying VPX tables initially by grabbing both the sounds and sound code from a “Fleeped” table and shoving them into a table that didn’t sound as good, but in time I began to modify and refactor the code as well as augment the sound collection with additional sounds that are specific to EM pinball tables. I ended up calling the package just EM Sounds.

I’m going to walk you through how to grab the sound code that I came up with as well as the library of sounds I am currently using and “EM Sound” Teacher’s Pet.

In this post, we’re going to move fast and break things!

(And then move a little more slowly … and fix the things we broke.)


Quick, just get in the car and do the following, I’ll explain later.

With the linted version of Teacher’s Pet, select the code beginning at Line 2567 (or if you have a different version of the script, find the Positional Sound Playback Functions banner and begin your selection to include it).

Where to begin your text selection.

And select up to Line 2671 (the end of the OnBallBallCollision subroutine).

Where to end your text selection.

Hit the Delete key or Cut from the menu. I typed xxx as a placeholder showing where the code vanished. Otherwise, you should have something that looks like the following:

After deleting the selected code.

That was easy. What we did was to remove a lot of code that we have a replacement for or will no longer need. But we have also broken the script. Let’s add our own positional sound code in the old one’s place.


The sound code I’m going to use is kept up to date on Github. Github is a site that contains mostly source code to computer programs. In fact it is where the sources to Visual Pinball live. If you click on this link you will be taken straight to the sound code file.

Selecting all the sound code.

Copy the text to the pasteboard by clicking the widget I indicate above. Go back to VS Code (Visual Studio Code) or wherever you are editing Teacher’s Pet and Paste all this code. Hit Save.

Pasting all the sound code.

If you scroll up to the end of HideOptions() you’ll see that our new code begins with a comment banner with EM Sounds.

Okay, we have our new sound code but the script to Teacher’s Pet is still broken. Now begins the task of fixing things. If you enjoyed linting in the previous post, you’ll love this.


First things first. EM Sounds has an initialization routine that we want to call before any sounds are played. It is called EMSInit() and it looks like this:

EMSInit routine.

I promised myself I would not dive into the code in this post but the above is so simple it serves as a gentle introduction to how this all works.

EMSInit() just has two lines of code. It expects you to pass it a parameter it will refer to as TableObj. It is only interested in the width and height properties of TableObj (TableObj.Width and TableObj.Height) — it assigns them to globals TableWidth and TableHeight.

Looking a few lines before, you can see that TableWidth and TableHeight have default values (1000 and 2000 respectively) but we want the real dimensions of the pinball table — and that is the purpose of this routine.

As we’ll see later, EM Sounds will use the width to decide how to pan a sound left and right, the height to decide how to fade a sound from front to back.

We want to call EMSInit() from somewhere in Teacher’s Pet.

If you recall back to the very first part in this series, we looked at the first subroutine in the script called Table1_Init(). Let’s scroll back up there again, near the top of the script file.

I’ve wrestled with how best to convey in this post all the small changes made in Teacher’s Pet to call the new sound code. The best way I could come up with was to do a file compare in VS Code between the previously linted version of Teacher’s Pet and the one I have modified for EM Sound support.

There is a green highlight in the “diff” (difference) below with a small plus sign (+) to the left. This indicates that this line of code was added.

Diff showing sound code changes.

This indicates that I inserted just one line before the call to LoadEM. That’s all that is needed to initialize the EM Sound code. EMSInit is the initialization routine, and we pass the routine the object representing the pinball table, Table1.


That was easy enough but what follows is a little more tedious. There are numerous places in Teacher’s Pet where the script calls a VPX function called PlaySound. For almost every instance we have a preferred routine in EM Sounds that we want to call instead. Each replacement EM Sounds routine is specifically tailored to a specific kind of sound. For example, we have a routine for when the player presses the left flipper button, another sound routine to call when the player releases the left flipper button. Etc.

Starting at the top of the script, use the code editor to search for playsound. Note that this time we are not searching case-sensitive as we did when linting.

First instance of playsound().

The very first instance (shown above) is commented-out code. Cool, we can ignore it. But did you notice in the search field that there are 144 instances of PlaySound? Yeah, get comfortable. Make some tea. Take breaks.

Continue searching and we get to four PlaySound calls in the routine Table1_KeyDown. Another diff follows. A red highlight indicates lines of code in Teacher’s Pet that were changed or removed, the green highlight indicates lines of code that were changed or added.

Diff showing sound code changes.

I’ll explain later, but the PlaySound calls that referred to a “buzz” sound were stripped out. The other PlaySound calls though I left but I commented them out. (I know, it’s subtle, but the diff shows green text indicating a “comment” — you can just see the small apostrophe at the start of each line of commented-out code). I chose to leave those calls as placeholders — temporarily.

The new code added are calls to EMSPlayLeftFlipperActivateSound and EMSPlayRightFlipperActivateSound. I pass an object to each of those calls: LeftFlipper and RightFlipper (respectively).

On to the next PlaySound

Diff showing sound code changes.

The above shows the simplest kind of substitution. Wherever the script used to want to play a coin sound, we have a specific call: EMSPlayCoinSound.

Notice that almost all the EM Sounds calls have had a form of EMSPlayXSound (where “X” might be “Coin” or “FlipperActivate”).


Diff showing sound code changes.

Above, replace with EMSPlayStartupSound.

The next instances of PlaySound are in the Table1_KeyUp function. First, the sound for releasing the plunger:

Diff showing sound code changes.

And then the counterpart to the flipper activated calls, we have EM Sounds calls for when the player releases the flipper buttons, or deactivates the flippers:

Diff showing sound code changes.

As before with the flipper calls, I left a couple lines of code as placeholders — commenting them out. We’ll revisit them later, but in case you’re already curious, it’s because those lines of code have references to something called “DOF”.

But lets get back to replacing PlaySound calls.

Diff showing sound code changes.

Above, if the ball goes down the drain (“hits” the drain object) pass the object Drain to EMSPlayDrainSound.

Right slingshot:

Diff showing sound code changes.

Left slingshot:

Diff showing sound code changes.

Again, for the slingshots I leave the original code in place but commented-out (because of “DOF” stuff).

Diff showing sound code changes.

There are three very similar bumper routines in Teacher’s Pet but I am showing you only the one above for Bumper1_Hit.

Since all three refer to DOF in their PlaySound calls, I am leaving the code in place but commented out. The only thing then that is really different between the three bumper routines is that the object passed to EMSPlayMiddleBumperSound is obviously Bumper2 in Bumper2_Hit and Bumper3 in Bumper3_Hit.

There are three different bumper routines in EM Sound — top, middle and bottom variants. They use different sound resources and so sound slightly different. You'll have to make a judgement call as to where you think the bumpers on the table you are editing are. I decided the Teacher’s Pet bumpers were more or less in the middle and so called EMSPlayMiddleBumperSound.

Moving on…

Diff showing sound code changes.

There are a bunch of trigger routines that look like the above. A simple substitution. Again for brevity I am only showing you one such example above.

The next run of PlaySound routines are for the kickers, of which there are quite a few in Teacher’s Pet. I did something a little different in the case of the kickers though. Below is for the first kicker — the rest of the kickers are similar.

Diff showing sound code changes.

Again leaving the original calls as placeholders, commenting out.

The original script called PlaySound with a sound called “scoopexit”. EM Sounds has a substitute routine for kickers called EMSPlaySaucerKickSound, but when I substituted this routine for a “scoopexit” in another VPX table, I had a player comment that the substitute sound did not ring true to the actual pinball table. So I thought for Teacher’s Pet I would stick with the “scoopexit” sound.

As you can see though, there is an EM Sounds routine I used called EMSPlaySoundAtVolumeForObject. I know it’s a long routine name, but maybe self-explanatory. You pass it the name of the sound (“scoopexit”) as well as the volume at which you want to play the sound and an object so it can “position” the sound. For the volume I created a constant.

Diff showing sound code changes.

Since there are a number of places following in the script for kicker sounds, having the one constant allows us to change the value of the volume in one place and it affects all three bumpers if we want to adjust it in the future.

Moving on. Below a simple substitution call for the knocker and click sounds.

Diff showing sound code changes.

“DOF” is in the knocker PlaySound call so again we leave it commented out anticipating a future change.

Next up: another simple substitution.

Diff showing sound code changes.

We’re almost done with the PlaySound stuff by the way.

Diff showing sound code changes.

EM Sounds has two flavors of sounds it plays for EMSPlayStartBallSound so we specify which one with an integer — zero (0) in the following case.

Diff showing sound code changes.

Substitute for "MotorLeer" (whatever that is) follows:

Diff showing sound code changes.

The other flavor of EMSPlayStartBallSound follows:

Diff showing sound code changes.

The diff for the subroutine PlayChime() was kind of a mess so I am not showing it to you. Instead, here is the finished code I substituted for the original code.

Diff showing sound code changes.

You’ll have to look at the old subroutine in Teacher’s Pet for yourself. It was quite a bit more complicated — an example of a Loserman76 boilerplate subroutine that could be used in a pinball table having three chimes or a table having two bells. When I researched Teacher’s Pet (on YouTube) I saw that it was a table with only two bells. So the replacement subroutine above is tailored to work only for the two-bell pinball table case.

Hey, that’s all the changes we need to make for PlaySound. The EM Sounds code follows and it makes calls to PlaySound but that is expected.

In the first post in this series we saw a comment banner for Object sounds in the script. There are some PlaySound calls there that I am going to leave for now. I will tell you that VPX will definitely halt when we play the table and one of those Object sounds routines are called. It will be instructive though to let VPX “crash” and then deal with them when it happens.

Near the bottom of the file there are a few more calls to PlaySound but they are not related to game-play. They’re just sounds that are triggered as you are entering your initials for a high score. There’s no advantage to change these over to use EM Sounds.

One more change we need to make so that VPX is happy though is shown below in a diff:

Diff showing sound code changes.

A global called tnob that we deleted earlier was replaced with one called NumberOfBalls in EM Sounds. That same variable is used in the shadow code, so we simply need to substitute the new variable name. (tnob was one of those obtuse variable names that bothered me — it took me way too long to realize that it stood for “the number of balls” so I renamed it.)

Back to VPX

While we just finished putting the script back together in a way that will make VPX happy, we’re not out of the woods yet. For the next steps however we’ll move back to the VPX environment. If you used a separate code editor like I did, it is time to Copy and Paste all your work back into the Script window in Visual Pinball.

To keep you (and me) from having to scroll so much in one blog post, I’m going to wrap this one up here. But continue to the next post, because we still need to add the new sound resources and fix a few more lines of the script to be able to enjoy our new sound code.