Skip to content

EXP Show Scripting

This functionality was added to FAST Expansion Boards in firmware version 0.10

This feature is not yet released

This documentation is for a feature that is not yet released. Details and features may change.

FAST Pinball expansion boards have the ability to run simple scripted shows directly on the EXP boards themselves. You can create your own shows and load them on a board, and then a show can be triggered via a simple command.

Here are some examples of things you could use this for:

  • Fade an LED through several colors.
  • Flash an LED on and off.
  • Flash, cycle, or "wig-wag" multiple LEDs in sequence.
  • Animate a series of LEDs.
  • Move a servo to a specific position.
  • Etc.

Each show can target multiple LEDs which you can pass to the show (as a block) when you trigger it. This means you can write a generic show (for example, to animate a circular motion across 4 LEDs) and then trigger it on different blocks of LEDs if you need the same show in multiple locations.

Shows can be very simple, or complicated. They support a powerful system of placeholders, variables, and comparison operators which allow you to write very flexible shows.

Shows are written to an expansion board via the ES: FAST Serial Protocol command. (Currently the shows are not saved to the board, but this will be added in a future release.) Then they're triggered with the TS: command.

Do you need to use shows?

FAST Pinball EXP shows are a great way to add some simple animations to your game, especially if you're writing your own software framework. If you use an existing higher level game framework, such as the Mission Pinball Framework, then you probably don't need to use the EXP show scripting. (MPF has its own show system which is much more powerful than the EXP show system.)

The most powerful feature of the EXP show scripting will be the ability to save shows to the EXP board's built-in flash memory, which would allow a show to automatically start playing when the board powers up. (This feature is not yet implemented, but will be added soon.) This will allow you to use the EXP show scripting to create simple "startup mode" shows for your machine that can start playing within a few seconds of power on, before the host computer is booted up. So you could do a cool full-playfield progress animation, or some other "proof of life" of the machine, while the host computer is starting.

These simple EXP shows are also nice if you have a few LEDs that you want to animate, but you don't want to have to write a full show in your game framework just to animate a few LEDs. For example, maybe you have a ring of LEDs on a pop bumper that you always want to animate in a certain way. You could write a simple EXP show to do that, and then trigger it from your game framework with a single command rather than having to write a full show in your game framework and trigger than full show every time you need it.

Another example is if you have a topper or some other mod with a FAST expansion board built-in. You could write an EXP show which automatically runs on power on, meaning the topper or toy could be animated and fun even if it's sitting on someone's shelf and not connected to a game.

Associated FAST Serial Commands

There are a few FAST Serial Protocol commands you can use to control the shows:

  • ER: Configures an LED block (groups arbitrary LEDs which are then targeted by a show)
  • ES: Writes a show to the board which can be triggered later
  • TS: Triggers (starts/stops) a show

See it in action

EXP shows are BETA and a work in progress

The EXP show scripting, as well as this documentation you're reading are both "beta" quality at the moment. They are being actively developed and you can certainly play with them now. Reach out to us in Slack if you have questions or feedback.

Let's run through a very simple example demonstrating an EXP show in action. This will certainly not make sense yet, so we'll explain everything afterwards. Here's an example using a FAST Serial Protocol terminal session via a connection made to the EXP port:

ea:48                       # Set active expansion board to the Neuron's built in one
id:                         # Confirm this EXP board is running at least firmware 0.10
ID:EXP FP-EXP-2000 0.10
es:0,(*)_b=ffL1+W10-W10J1.  # Create Show 0 which blinks all LEDs blue/off every half second
er:4,0,0,1,2,3,6,7,8        # Define LED Block 4 to contain LEDs 0-3 and 6-8
ts:0,4,0                    # Trigger Show 0 to run on LED Block 4

If you want to see this in action, plug a chain of LEDs into the Neuron's first LED port. You should see the first four LEDs, and LEDs 7-9 blink blue/off about every half second.

Let's dig into how the show commands work.

Show structure

An EXP show consists of an arbitrary number of commands followed by a mandatory end of show marker (a period). There are two types of commands:

  • Single character command, which will have a constant number of arguments (and be a known length), or
  • Variable-length commands, which have opening and closing characters to support the arbitrary number of arguments in between.

Target LED block

EXP shows are generic, in that they can work on any arbitrary set of LEDs. This means that when you trigger a show, you need to tell it which LEDs you want to target. This is done by passing along a "target block" when you trigger the show. The target block can be one of the following:

  • A value of 0 to 3, which corresponds to the four hardware LED ports on the EXP breakout board, or
  • A value of 4 to F (4-15 decimal) which corresponds to a user-defined LED block (which you can define with the ER: command) which can be any random combo of up to 32 LEDs across any of the four hardware LED ports.

Shows target EXP breakout boards

Recall from the EXP Programming Guide that expansion board addresses are two hex digits, with a third digit for the breakout board number. All of the EXP show targeting and LED blocks are local to the specific breakout board.

Specifying the active LED group within the show

The target LED block described above specifies which LEDs are part of the show. The active LED group is a subset of the LEDs in the target block which represents the LEDs being affected by the specific command in the specific step of the show.

For example, if you wanted a script which alternately flashed two groups of two LEDs (like a railroad crossing sign), you might use the ER: command to specify a block of LEDs containing LEDs 0-3. Then you'd change which LED was active in each step of the show. So the show might look like this:

  1. Set LEDs 0-1 to be the active LED group, send a command to turn the active group red.
  2. Set LEDs 2-3 to be the active LED group, send a command to turn the active group off.
  3. Wait
  4. Send a command to turn the active group off (since LEDs 2-3 are still the active group from the previous step)
  5. Set LEDs 0-1 to be the active LED group, send a command to turn the active group off.
  6. Wait
  7. Repeat

In the logical example above, there are four LEDs in the target block, but only two LEDs in the active LED group at any given time. To use this show, you'd trigger it with the TS: command and pass in the target block (whatever block number you used when you assigned the four LEDs to the block with the ER: command), and then two of those LEDs would be the active ones for each step.

General show syntax

Here's a quick reference of the types of commands used in shows, as well as the valid syntax for each command. We'll go into more detail on each command below.

  • <leds> = <number> or <group>
  • <color> = <color_var> or <number><number><number>
  • <number> = 2 digit hex (00 - 1F) or <byte_var> or <ro_byte_var>
  • <label> = Single character digit 1 through 8
  • <comparison> = Single character operator =, >, <, !, or &
  • <assignment> = Single character operator =, +, -, *, /, %, &, |, or ^
  • <byte_var> = Underscore plus single digit 1-8 (_1 through _8)
  • <ro_byte_var> = Underscore plus single letter _x, _y, _z, _i, _d, _a, or _n
  • <color_var> = Underscore plus single letter _r, _g, _b, _w, _h, _s, or _v

Show Commands

Basic flow control

  • . End of Show
  • L Set a label (so you can jump back to this point later): L<label>
  • J Jump to a previously set label location: J<label>
  • W Wait (in ticks, each tick is 32ms): W<number> (example: W40 = wait 40 ticks, 40 hex is 64 ticks or 2 seconds)
  • P Priority (not yet implemented) P<number>
  • F LED fade (in ticks, each tick is 32ms): F<number>

Advanced flow control

  • {...} Block: encloses optional section for use with logical test
  • ? Test: `? {...}``

Basic LED commands

  • + - On : Turn on LED group
  • - - Off : Turn off LED group

Extended non-LED stuff

  • E - Send Event: E<number><number><number><number>
  • G - Set GI Level: G<number><number>
  • M - Run Motor:
  • D - Pulse Driver:

Read-only byte variables

  • _x, _y, _z - position
  • _i - index in target block

Component variables

  • _h Hue (wraps rather than clamped)
  • _s Saturation
  • _v Value
  • _r Red
  • _g Green
  • _b Blue
  • _w White - added but disabled
  • _t Color table entry

Byte variables

  • _1 ... _8 - locals

Color variables

  • _c - color (also a “component” var)
  • _9, _0 - locals

Variable manipulation

  • <byte_var> - Assign : <byte_var><assignment><number>
  • <color_var> - Assign : <color_var><assignment><color>
  • (group)<component_var> - Assign : (group)<component_var><assignment><number>

Active LED group

The indices of the LED group are all relative to the target block that was defined for the show (port, user block, etc.) and currently the show is limited to only the LEDs defined as that block.

  • (*) Set all LEDs in the target block as the active LED group
  • (<number><number>...) Set a list of LEDs as the active LED group
  • (+<number><number>...) Add a list of LEDs to the active LED group
  • (-<number><number>...) Remove a list of LEDs from the active LED group
  • (?<component_var><comparison><number>) Set the active LED group to the LEDs which match the test
  • ([<number><number>]) Set the active LED group to the LEDs in the range (all LEDs from the first to the second number, inclusive)
  • (!) Invert the active LED group (remove all LEDs in the active LED group from the target block, and add all LEDs in the target block that are not in the active LED group)

End of Show

The end of show marker (EOS) is a period . character and valid any time a command identifier would be expected.

Command List

Command Name Arguments Example Explanation
L Label <label> L1 Labels this point in the script as "1"
J Jump <label> J1 Jumps to the point in the script labeled "1"
W Wait <number> W40 Waits 40 ticks (40 hex = 64 ticks = 2 seconds)
P Priority <number> P01 Not yet implemented
F Fade <number> F40 Fades the active LED group over 40 ticks (40 hex = 64 ticks = 2 seconds)
+ On <LEDs> + Turns on the active LED group
- Off <LEDs> - Turns off the active LED group
? Test <byte_var><comparison><number> ?_1=1 Tests if byte variable 1 is equal to 1
_ Assign <assignment><number> _1=01 Assigns byte variable 1 to 1
_ Assign <assignment><color> _r=ff Assigns color variable r to ff
_ Assign <assignment><number> _h=1 Assigns component variable h to 1
(*) All <LEDs> (*) Sets all LEDs in the target block as the active LED group
( Start a group definition <LEDs> (000102) Defines a group of LEDs 0, 1, and 2 (you could optionally add commas as they are ignored, but they count towards the 255 max char length. Numbers must be two digits though.
) Close a group definition <LEDs> (000102) Defines a group of LEDs 0, 1, and 2
{ Start a block <LEDs> {?_1=1 Starts a block which will only run if byte variable 1 is equal to 1
} Close a block <LEDs> {?_1=1} Close a block which will only run if byte variable 1 is equal to 1
. End of show None . Ends the show

Whitespace characters ignored by parser

These characters still count towards the 255 character max show length, but they are ignored by the show parser.

  • Space
  • , Comma
  • \r Carriage return
  • \n Line feed
  • \t Tab


  • = Assign
  • + Add
  • - Subtract
  • * Multiply
  • / Divide
  • % Modulo
  • & Bitwise AND
  • | Bitwise OR
  • ^ Bitwise XOR


  • = Equal
  • ! Not equal
  • > Greater than
  • < Less than
  • & Bitwise AND
  • [ Range start
  • ] Range end


Byte vars for show

1 = '1', 2 = '2', 3 = '3', 4 = '4', 5 = '5', 6 = '6', 7 = '7', 8 = '8',

Color vars for show

9 = '9', 0 = '0',

T = 't', - is the trigger data, which is the flags from the event. Any use???

C = 'c', N = 'n',

Component vars for LEDs

H = 'h', S = 's', V = 'v', R = 'r', G = 'g', B = 'b', W = 'w',

Read only vars for LEDs

A = 'a', I = 'i', D = 'd', X = 'x', Y = 'y', Z = 'z'


SET = '=',

ALL = '*', INVERT = '!', INCLUDE = '+', EXCLUDE = '-', QUERY = '?',


Now that you've seen the syntax, let's look at some examples. First, let's look at the show we used at the beginning of this section:


That first part of that line is the command es:, and the 0, just assigns this show script to be Show 0, so the (*)_b=ffL1+W10-W10J1. is the actual show. Breaking it down:

(*)     # Set all LEDs in the target block as the active LED group
_b=ff   # Set blue channel of the active LED group to `ff` hex = 255 decimal = full on
L1      # Set a label (1) here so we can jump back to this point later
+       # Turn on the active LED group (which is all LEDs in the target block)
W10     # Wait 10 ticks (10 hex = 16 ticks = 0.5 seconds)
-       # Turn off the active LED group
W10     # Wait 10 ticks (10 hex = 16 ticks = 0.5 seconds)
J1      # Jump back to the label 1 we set earlier
.       # End of show


_1=00   # Set byte variable 1 to 0
L1      # Set a label (1) here so we can jump back to this point later
(_1)    # Set the active LED group to the LEDs in the target block which match the value of byte variable 1
_b20    # Set the blue channel of the active LED group to 20 hex = 32 decimal = 1/8 on
+       # Turn on the active LED group
W20     # Wait 20 ticks (20 hex = 32 ticks = 1 second)
_1+01   # Add 1 to byte variable 1
J1      # Jump back to the label 1 we set earlier

_0 and _9 are the same except they are colors so _0=00FF00(*)_c=_0 2:52 _1 through _8 can also be initialized optionally as part of the TS: command at the end

N or > jump the next page, P or < for previous, search with S or ?