Two independently enabled plugins for Visual Pinball X:
| Plugin | Purpose | Connection |
|---|---|---|
MPF Bridge (mpf) |
Switches, coils, lamps and MPF-driven game logic | BCP, normally localhost:5051 |
MPF GMC Media (mpfmedia) |
Live GMC video on VPX Flashers | Local TCP from mpf-gmc-media |
The controller replaces the Windows-only
mpf-vpcom-bridge using VPX's
native plugin SDK. It retains CreateObject("MPF.Controller"); the VBScript
helper below adapts Changed* responses for existing table scripts.
The video plugin is optional. Switch/coil control works without GMC or the media service. The video plugin does not open a BCP connection or poll controller outputs. Neither plugin distributes game tables or assets.
CI builds and tests both plugins on these native runners:
| Platform | Archive suffix | Controller binary | Video binary |
|---|---|---|---|
| Windows amd64 | windows-amd64 |
plugin-mpf64.dll |
plugin-mpfmedia.dll |
| macOS arm64 | macos-arm64 |
plugin-mpf.dylib |
plugin-mpfmedia.dylib |
| Linux amd64 | linux-amd64 |
plugin-mpf.so |
plugin-mpfmedia.so |
| Linux arm64 | linux-arm64 |
plugin-mpf.so |
plugin-mpfmedia.so |
Each successful job provides mpf-plugin-<platform>.tar.gz and
mpfmedia-plugin-<platform>.tar.gz, containing mpf/ and mpfmedia/ respectively.
Download development artifacts from Actions
or tagged builds from Releases.
Older releases may contain only the controller and use earlier archive names.
The two-plugin archives become release assets when a new version is tagged.
The SDK is pinned to candidate v10.8.1-3788-2151290. Source compatibility
metadata has no natively validated VPX builds yet, and current macOS application
metadata cannot establish that exact build. macOS CI targets macOS 15 or newer.
Windows/Linux CI checks compilation and synthetic transport, not full live-game compatibility.
Intel macOS and 32-bit Windows are outside this CI matrix.
See compatibility before preparing a candidate archive.
Close VPX, download the controller archive for your architecture, and extract
its mpf/ folder into your VPX plugins directory:
| Platform | Plugins directory |
|---|---|
| macOS | VPinballX_BGFX.app/Contents/Resources/plugins/ |
| Linux | <vpx-install>/plugins/ |
| Windows | <vpx-install>\plugins\ |
After installation you should have:
plugins/
mpf/
plugin.cfg
plugin-mpf.dylib (macOS)
plugin-mpf.so (Linux)
plugin-mpf64.dll (Windows 64-bit)
VPX comes in two flavors that affect how you configure plugins:
Windows with editor — open VPX, go to Preferences > Plugins, find "MPF Bridge" in the list, and enable it. Recording and recording path can also be configured from this screen.
Standalone player (macOS, Linux, Windows standalone) — the standalone player has no settings UI. Edit VPinballX.ini directly and add:
[Plugin.MPF]
Enable = 1The ini file is located at:
| Platform | Path |
|---|---|
| macOS / Linux | ~/.vpinball/VPinballX.ini |
| Windows | VPinballX.ini in the VPX install directory |
On macOS, VPX must be signed with library validation disabled for third-party plugins to load. If your VPX copy was re-signed locally, re-sign it with:
codesign --force --sign - --entitlements entitlements.plist \
--preserve-metadata=identifier,requirements,flags,runtime \
~/Applications/VPinballX_BGFX.appWhere entitlements.plist contains:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
</dict>
</plist>Copy mpf_controller.vbs from the installed mpf/ directory (or
scripts/mpf_controller.vbs in this source checkout) into the same folder as your .vpx table file (or any directory VPX can load scripts from).
This helper converts the plugin's object-based Changed* responses into the 2D variant arrays that existing table scripts expect.
Add this line near the top of your table script (after any existing ExecuteGlobal lines):
ExecuteGlobal GetTextFile("mpf_controller.vbs")Then replace the direct Controller.Changed* calls in your timer sub:
| Before | After |
|---|---|
Controller.ChangedLamps |
MPF_ChangedLamps(Controller) |
Controller.ChangedSolenoids |
MPF_ChangedSolenoids(Controller) |
Controller.ChangedGIStrings |
MPF_ChangedGIStrings(Controller) |
Controller.ChangedLEDs |
MPF_ChangedLEDs(Controller) |
Controller.ChangedFlashers |
MPF_ChangedFlashers(Controller) |
Controller.HardwareRules |
MPF_HardwareRules(Controller) |
All other Controller methods (Switch, PulseSW, IsCoilActive, Run, Stop, Mech) work unchanged.
In your MPF machine config:
hardware:
platform: virtual_pinball- Start MPF:
mpf both(the media controller satisfies the BCP client connection MPF waits for before starting its own BCP server). - Start VPX and load your table.
- The table script's
CreateObject("MPF.Controller")is handled by this plugin. Controller.Runconnects to MPF's BCP server (default:localhost:5051).
To specify a custom address/port in your table script:
Controller.Run "192.168.1.100", 5051-
Install and start
mpf-gmc-mediawith its Godot exporter. The media service README describes its separate runtime requirements. Cockpit can manage this service and generate.mpf-cockpit/media.jsonplusmedia.vpx.ini. -
Close VPX and extract the video archive's
mpfmedia/directory alongsidempf/in the plugins directory. Enable it in VPX preferences:[Plugin.MPFMedia] Enable = 1
-
Run
mpf-cockpit upbefore launching VPX directly, so the media service is available. Place the local connection file next to the table. Cockpit generates.mpf-cockpit/media.vpx.inialongsidemedia.json; copy that.vpx.inifile to the table directory asmedia.vpx.ini, or keep the table in the project root so its.mpf-cockpit/directory is found automatically. You can now launch the table from VPX's graphical interface. -
Configure each Flasher's display source using the configured numeric source ID:
ctrl://MPFMedia/display?id=0,ctrl://MPFMedia/display?id=1, etc. Size the surfaces to preserve each source's aspect ratio.
On each table start, the plugin uses the VPX SDK's table path and searches in this order:
GMM_CONFIG, when explicitly set (amedia.jsonpath selects its companionmedia.vpx.ini; a.vpx.inipath can also be supplied directly).<table-name>.media.vpx.inibeside<table-name>.vpx.media.vpx.iniin the table directory..mpf-cockpit/media.vpx.iniin the table directory.
The first existing configuration wins; an invalid explicit selection does not silently fall back to another project. The plugin stops its media connection when the table closes and resolves configuration again for the next table. It does not depend on the graphical launcher's working directory.
media.json remains the service configuration; the plugin reads its generated
INI companion. Keep the connection file local: it contains an authentication
token and should not be embedded in or distributed with a table. If the service
configuration changes, update the copied connection file too.
Both plugins can run together, or the video plugin can run alone with a display-only table.
Video is received as raw RGB frames, with no encoding in the VPX plugin. Only subscribed sources are requested. Audio remains in Godot; recording and encoding belong to the separate media service. If the service disconnects, the plugin retries while displaying black or its last frame. See media integration details.
The plugin can record all BCP traffic during a game session for debugging and test generation.
Windows with editor — enable recording and set the output path from Preferences > Plugins > MPF Bridge.
Standalone player — add these settings to VPinballX.ini:
[Plugin.MPF]
Enable = 1
EnableRecording = 1
RecordingPath = /path/to/recordingsRecordings are saved as JSONL files (one JSON object per line), named YYYY-MM-DD_HH-MM-SS_mpf_recording.jsonl.
Each event has a category for filtering:
| Category | Direction | Description |
|---|---|---|
input |
vpx_to_mpf | Switch changes, pulse events, mech writes |
state |
both | Polled state: solenoids, lamps, LEDs, GI, flashers, hardware rules, coil active checks |
query |
both | Read-only queries: switch reads, mech reads |
Example events:
{"ts":0.000,"wall":"2026-04-15T17:33:56.506674Z","cat":"input","dir":"vpx_to_mpf","cmd":"set_switch","params":{"number":"2","value":"bool:True"}}
{"ts":2.364,"cat":"state","dir":"mpf_to_vpx","cmd":"changed_solenoids","result":[["coil1",true]]}Requirements: CMake 3.20+, a C++20 compiler, Python 3 for the synthetic media transport test, and Git to fetch the pinned SDK.
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build --config Release
ctest --test-dir build --output-on-failure -C ReleaseBoth plugins build by default into build/dist/mpf/ and build/dist/mpfmedia/.
To build only the video consumer, use
cmake --build build --config Release --target mpf-media-plugin.
The media transport test loads the real dynamic library and sends synthetic
frames over an isolated loopback connection; it does not connect to a game.
To build against a specific VPX version:
cmake -B build -DVPX_TAG=v10.8.1-3788-2151290Both plugins use GPLv3 — see LICENSE. The pinned VPX SDK headers are marked GPLv3+. The separate media service retains its own license. Distribution archives include this repository's license text.