A digital mini-marquee for the Neo Geo MVS. MarqueeMark replaces a mini marquee card with a small LCD panel. With a TerraOnion NeoSD Pro flash cart, it always shows the correct game, switching the marquee art the instant you load or change one, no NeoSD Pro required if you'd rather just pick the game by hand instead (see Using MarqueeMark without a NeoSD Pro). It also serves a live "now playing" overlay for OBS so your stream always shows what's running.
No original hardware is modified. The panel mounts to the back of the marquee plexi the same way the paper cards did, and everything is reversible.
- Automatic game detection via the NeoSD Pro's USB serial interface (a previously undocumented protocol, see How it works)
- Instant art switching with a smooth fade, including when swapping virtual slots on the cart
- Power-cycle aware: the cart always auto-boots Flash Slot 1 after power-off, and MarqueeMark shows the right art within seconds of the cabinet powering on, before you touch anything
- Display sleep: when the cabinet turns off, the panel's backlight shuts down too; it wakes automatically with the cab
- Calibration from your browser: position, resize, tilt, and flip the image from the admin page on any phone or PC while watching the panel update live. No SSH, no keyboard, no Linux required. Proportions are locked to the real mini-marquee card, so the image can never be stretched.
- Second marquee support: a slot with a real cartridge instead of a NeoSD Pro can't announce itself, so a second panel (on its own Pi) can be set to manual mode instead. Pick its art from the same kind of admin page, and it can pull its whole art library from the primary Pi and mirror its sleep state automatically, or be put to sleep by hand.
- OBS stream overlay: a browser source URL that shows the current game's mini-marquee art in the corner of your stream, updating live
- Browser art manager: drag and drop your marquee PNGs onto a web page to install them
- Runs headless as a systemd service; survives crashes, USB unplugs, and power cycles
| Part | Notes |
|---|---|
| 8" IPS LCD panel kit (1024x768, HDMI driver board) | Chimei Innolux HJ080IA-01E class. Active area covers the standard 4.44" x 5.44" mini-marquee window with overscan to hide the bezel. Includes its own USB-to-barrel power lead, which can run from one of the Pi's USB-A ports. |
| CanaKit Raspberry Pi 4 Starter Kit (2 GB) | One box covers the Pi 4 Model B, the correct micro-HDMI display cable, a proper 3.5A USB-C power supply, a 32 GB microSD card, case, heatsinks, and an inline power switch. 2 GB of RAM is plenty; the Pi only renders images. Reflash the included SD card per step 1 (skip the pre-loaded image), and skip installing the fan: heatsinks alone are enough for this workload, and a fan just pulls cabinet dust through the case. One Pi per panel; see Adding a second marquee if your cabinet has more than one slot. |
| Double-sided mounting tape | Final panel mounting to the back of the marquee plexi. |
| Painter's tape | Temporary mounting while you align and calibrate; commit to the strong tape only after calibration looks right. |
| USB-A to Micro-USB cable, 5 ft | Pi to the NeoSD Pro's USB port (the cart uses Micro-USB). Only needed for a panel following a NeoSD Pro; a manual-mode panel doesn't need one. |
| TerraOnion NeoSD Pro (optional) | Enables automatic game detection. MarqueeMark reads its game announcements; it does not modify the cart in any way. Not required at all if you use --manual mode; see Using MarqueeMark without a NeoSD Pro. |
The hardware links above are Amazon affiliate links, buying through them supports this project at no cost to you.
Marquee art is not included. Mini-marquee art packs
using MAME short-name file naming (mslug.png, kof95.png, ...) are
available to registered users at EmuMovies.
https://emumovies.com/files/file/1628-neo-geo-mvs-marquee-pack-mini/
Add the PNGs from the built-in art manager page. I include a "generic.png" image of the Gamesboro logo so that you can align your image before downloading the pack.
The NeoSD Pro's USB port appears as a standard serial device
(/dev/ttyACM0, STM32 CDC, VID 0483 PID 5740) powered by the cartridge
slot. It enumerates when the cab is on and vanishes when it's off.
Whenever a game is loaded (menu load, RAM load, or virtual-slot switch), the cart spontaneously broadcasts a 61-byte frame. As far as we know this protocol was previously undocumented; it was reverse-engineered for this project in July 2026:
offset 0-2 magic 99 88 3A
offset 3-4 u16 LE zero-based menu slot index
(Flash Slots 1-4 announce as 0-3, RAM slot as 4)
offset 5-6 u16 LE game's index in the SD card library list
offset 7-8 u16 LE NGH catalog number, BCD-encoded (0x0269 = NGH-269)
offset 9-10 reserved (zero)
offset 11-43 short name - 33-byte field, null-terminated, stale bytes
after the terminator (reused buffer)
offset 44-60 full title - 17-byte field, may be truncated with NO
terminator
Notes: RAM loads may announce twice (MarqueeMark dedupes). The RAM slot's contents are destroyed at power-off and the cart always auto-boots Flash Slot 1. MarqueeMark tracks what lives in each flash slot so the marquee is correct from power-on. The cart never speaks during auto-boot, which is why that tracking exists.
MarqueeMark itself is one Python file: a serial listener (or, in manual mode, an admin-page selection instead), a pygame renderer that draws directly to the display (no desktop needed), a small state store, and an HTTP server for the admin page and OBS overlay.
Three steps: flash the card, run one command, reboot. You do not need to know Linux, and after the reboot everything else happens in a web browser. This is the recommended path for everyone; a manual, step-by-step version is documented further down under Manual installation for reference or customized setups.
- Download Raspberry Pi Imager from raspberrypi.com/software (Windows, macOS, and Linux) and install it.
- Open Imager and set:
- Choose Device: Raspberry Pi 4
- Choose OS: Raspberry Pi OS (64-bit) (the top recommended option once a device is picked; this specific 64-bit build is required)
- Choose Storage: your microSD card
- Before writing, click the settings gear (OS customisation) and set a
hostname (
marqueeis used in the examples below), enable SSH, and add your Wi-Fi credentials. Setting the hostname here is worth doing either way: it's what makesmarquee.localwork later. - Write the image.
Pick whichever is easier for you. Both end up in the same place.
Option A: with a keyboard and monitor (easiest, no SSH)
Do this at a desk before taping the panel into your marquee, so the screen is in its normal landscape orientation and the desktop is readable. Once the panel is mounted in portrait the desktop will be sideways, which is survivable but unpleasant to type against.
- Connect the panel (or any HDMI monitor), a USB keyboard, and power up the Pi. It boots to the desktop.
- If you didn't set Wi-Fi in Imager, connect now using the network icon in the taskbar.
- Press Ctrl+Alt+T to open a terminal.
- Type the install command:
curl -fsSL https://raw.githubusercontent.com/beastech/marqueemark/main/install.sh | bashIf you'd rather not type that by hand, open Chromium on the Pi, go to this project's GitHub page, copy the command, and paste it into the terminal with Ctrl+Shift+V (in a Linux terminal, plain Ctrl+V does not paste).
The installer reboots the Pi when it finishes, so expect the desktop to disappear and the marquee software to take over the screen.
Option B: headless over SSH
Boot the Pi with no monitor attached, then from another computer:
ssh <username>@marquee.localOnce connected, run the install command:
curl -fsSL https://raw.githubusercontent.com/beastech/marqueemark/main/install.sh | bashWhen the installer finishes it counts down from 10 and reboots the Pi
automatically. This is required, not cosmetic: the Pi has to boot to the
console instead of the desktop for the display to work, and your new
permissions take effect at the same time. If you press Ctrl+C to cancel
the countdown, run sudo reboot yourself before using the web interface,
or it will refuse the connection.
If you installed over SSH, your connection will drop during the reboot. Reconnect after about 30 seconds, or just move to your browser; you're done with the terminal either way.
You are never asked about the panel's rotation. If the image comes out upside down, one button on the admin page fixes it (see Calibrating the image).
That's the whole install. Everything from here happens in a browser on
any device on your network: open http://marquee.local:8080/admin to
add art and calibrate the image. See
Using the admin page below.
Can't reach marquee.local? Use the Pi's IP address instead. Run
hostname -I on the Pi to see it, then browse to
http://THAT_ADDRESS:8080/admin. Note that if you never set a hostname
in Imager, the default is raspberrypi.local, not marquee.local.
To update to the latest version, run the same install command again:
curl -fsSL https://raw.githubusercontent.com/beastech/marqueemark/main/install.sh | bashOn a re-run the installer downloads the current version, keeps any
options you added to the service (such as --idle generic or
--keep-awake), and restarts the service instead of rebooting. Your art,
calibration, and slot history are left untouched.
If you calibrated before this update, the arrow keys may need a quick recheck the first time you open calibration afterward. See D-pad orientation below; it's a one-time fix, not something that needs redoing on every update.
The installer deliberately does not upgrade your operating system. A full upgrade can take 10 to 20 minutes on a Pi, can stop to ask questions mid-install, and isn't needed for MarqueeMark to run. That choice is left to you.
This Pi will likely sit on your network for years, though, so it's worth keeping patched. To bring it up to date once, whenever you like:
sudo apt update && sudo apt full-upgrade -yTo have it install security updates automatically from then on, one command sets it up:
sudo apt install -y unattended-upgradesOn Raspberry Pi OS that enables daily security updates with no further configuration. It runs quietly in the background and won't interrupt the marquee.
MarqueeMark's admin page has no password. Anyone who can reach the Pi on your network can upload art, delete files, change the calibration, and sleep or wake the display. That's a deliberate trade for ease of setup, and it's fine on a normal home network.
Do not port-forward this device or expose port 8080 to the internet. It is designed to be reached only from inside your own network.
Open http://YOUR_PI_HOSTNAME.local:8080/admin from any browser on
your network, phone or PC. Everything you need after installation lives
here, and none of it requires SSH.
Note: the bare address (http://YOUR_PI_HOSTNAME.local:8080/) serves the
OBS overlay, not the admin page. Include /admin.
Drag and drop your PNG files onto the drop zone. The page shows every
installed marquee as a thumbnail, lets you delete files, and warns you if
generic.png (the fallback image) is missing.
Files must be named by MAME short name (mslug.png, kof95.png, ...);
only PNGs are accepted, and names are sanitized automatically. Because
the art lives on the Pi's Linux partition, this page is also the easiest
path from a Windows PC: no SD-card readers or SFTP tools required.
If you're not sure what a game's short name is, you don't have to look it
up: load the game on the NeoSD Pro and MarqueeMark logs the exact name it
wants (journalctl -u marqueemark -n 5), or just watch which art fails to
appear.
A Sleep and Wake button live on the admin page for every panel. Use them to blank the panel and let its backlight switch off, or bring it back, whenever you like. Sleeping by hand stays in effect even if the cabinet powers on again; only Wake, or the automatic wake for a panel following a NeoSD Pro, clears it.
This is the only way to control sleep on a manual-mode panel with no
--sleep-source configured, since it has no cabinet-power signal to
follow on its own.
Mount the panel behind the marquee window with painter's tape first, positioned so live pixels overhang the window opening on all four edges. Then click Start Calibration on the admin page. A test pattern appears on the panel and the controls become active. Watch the physical marquee while you click; it updates live.
- Arrow pad: moves the image up, down, left, and right.
- Center button: cycles the nudge step (5px, 1px, 20px). Start coarse, finish on 1px.
- Size + / -: grows and shrinks the image. Proportions are locked to the real 4.44" x 5.44" mini-marquee card, so the image can never be stretched or distorted.
- Tilt buttons: rotate the image in 0.1° and 0.5° steps. Use this if the panel ended up slightly crooked when you taped it; there is no need to re-tape.
- Flip 180°: use this if the image is upside down. This is saved with the rest of your calibration, so it survives reboots.
- D-pad orientation: which way the arrow pad needs correcting depends on which edge your panel's ribbon cable exits, which varies between panels and can't be figured out automatically. If pressing an arrow moves the image a direction other than the one you pressed, click this button and try again. It cycles through four settings; one of them will be correct. This is a one-time setting per panel, saved with the rest of your calibration.
- Preview: switches between the alignment test pattern and real marquee art, for a final check of how it actually looks.
- Save stores everything; Cancel discards it and returns the marquee to normal.
Aim to have the pattern slightly overfill the window opening on all four sides, so no black edge is visible through the plexi. When it looks right, commit the panel with the double-sided tape and re-run calibration for a final touch-up if the panel shifted.
You don't need a NeoSD Pro, or any flash cart, to use MarqueeMark at all.
--manual mode works perfectly well as your only panel, on any MVS
cabinet, real cartridges included. The tradeoff is simple: instead of the
marquee updating itself automatically when you swap a cart, you pick the
game from the admin page, and it stays showing that until you change it.
This is a good fit if you swap cartridges rarely, if you'd rather not buy a flash cart just for this project, or if you just want a nicer marquee than the paper card without needing automatic detection at all.
Setup is the same install as anywhere else in this README, with one
difference: skip the NeoSD Pro's USB cable entirely, and add --manual
to the service's ExecStart line:
--manual
That's it. No --art-source or --sleep-source needed unless you're
running a second panel too (see below). Upload art directly to this
panel's own admin page, pick a game from the Now showing dropdown,
and use the Sleep and Wake buttons there whenever you turn the
cabinet on or off, since without a NeoSD Pro there's no automatic signal
for MarqueeMark to follow.
Some cabinets have more than one cartridge slot, and only one can hold a NeoSD Pro. A second slot with a real cartridge in it has no way to announce itself, so MarqueeMark can't auto-detect what's loaded there the way it does for a NeoSD Pro. A second panel handles this in manual mode instead: you pick what it shows from its own admin page, and it stays on that selection until you change it.
This runs on its own Raspberry Pi, one Pi per panel. A single Pi can only own one display at a time under the display driver MarqueeMark uses, so a second panel needs a second Pi rather than a second cable into the first one.
Install MarqueeMark on the second Pi the same way as the first (Step 1 and Step 2 above), then edit its service to add:
--manual --art-source http://marquee.local:8080 --sleep-source http://marquee.local:8080/current
replacing marquee.local with your primary Pi's actual hostname or IP.
--manualturns off the serial listener and switches this panel to admin-page selection instead. Its own admin page gains a Now showing picker at the top.--art-sourcepoints this panel at the primary Pi's art library, so you only maintain one set of PNGs. This panel downloads and caches whatever it needs and falls back to its local cache if the primary is unreachable.--sleep-sourcepoints at the primary's/currentendpoint, which is read-only, so the primary needs no changes at all. When it reports no game running, the cabinet is off, and this panel sleeps too; when it reports a game again, this panel wakes automatically.
Both flags are optional. Without --art-source, upload art directly to
this panel's own admin page instead. Without --sleep-source, use the
Sleep and Wake buttons on this panel's admin page by hand.
MarqueeMark serves a transparent overlay page showing the current game's mini-marquee art in the bottom-right corner, updating live.
In OBS: Add > Browser Source, URL
http://YOUR_PI_HOSTNAME.local:8080/overlay, width 1920, height 1080.
That's it. When no game is identified, the overlay shows generic.png.
Also available: http://...:8080/current returns the current game as
JSON, if you want to build your own integrations. This is also what a
second panel polls for --sleep-source.
You do not need any of this if you used the quick install above. These are the same steps the installer performs, written out for anyone who wants to do it by hand or adapt it to a different setup.
Flash Raspberry Pi OS (64-bit) with Raspberry Pi Imager. In the
imager's settings, set a hostname (e.g. marquee), enable SSH, and add
your Wi-Fi credentials so the Pi is reachable headless from first boot.
Boot the Pi and SSH in. Optionally bring the OS up to date first (see Keeping the Pi updated); it isn't required for MarqueeMark.
Set the Pi to boot to the console (no desktop, MarqueeMark draws to the screen directly):
sudo raspi-config nonint do_boot_behaviour B1sudo apt install -y python3-serial python3-pygame
sudo mkdir -p /opt/marqueemark/art
sudo chown -R $USER:$USER /opt/marqueemarkCopy marqueemark.py into /opt/marqueemark/. Art is added later from
the admin page, no file-transfer tools needed.
Give your user serial and display access (log out and back in after):
sudo usermod -aG dialout,video,render,input $USERAllow the display-sleep feature to control panel power (one specific command only, this is not general sudo access):
echo "$USER ALL=(root) NOPASSWD: /usr/bin/tee /sys/class/graphics/fb0/blank" \
| sudo tee /etc/sudoers.d/marqueemark
sudo chmod 440 /etc/sudoers.d/marqueemark- USB cable: Pi to the NeoSD Pro's USB port. The cart only powers up with
the cabinet, so don't worry if nothing appears until the cab is on.
Skip this if the panel is running in
--manualmode. - HDMI: Pi to the panel driver board. Panel's power: driver board's USB-to-barrel cable to one of the Pi's USB-A ports.
- With the cab on, verify the cart enumerates:
ls /dev/ttyACM0Create /etc/systemd/system/marqueemark.service:
[Unit]
Description=MarqueeMark digital marquee
After=multi-user.target
[Service]
User=YOUR_USERNAME
SupplementaryGroups=video render input dialout
Environment=SDL_VIDEODRIVER=kmsdrm
Environment=SDL_AUDIODRIVER=dummy
Environment=PYTHONUNBUFFERED=1
WorkingDirectory=/opt/marqueemark
ExecStart=/usr/bin/python3 /opt/marqueemark/marqueemark.py --rotate 90
Restart=always
RestartSec=3
[Install]
WantedBy=multi-user.target(Replace YOUR_USERNAME with yours. For a second, manual-mode panel, add
the flags described in Adding a second marquee
to the ExecStart line.) Then:
sudo systemctl daemon-reload
sudo systemctl enable --now marqueemarkWatch it live:
journalctl -u marqueemark -fFrom now on the Pi boots straight into MarqueeMark. Power the cab on and the marquee shows the Flash Slot 1 game automatically; switch games and it follows; power the cab off and the panel blanks, then sleeps its backlight ~10 seconds later.
Note on --rotate: this only sets the starting orientation. Once you use
the Flip 180° button on the admin page, that choice is saved in
calibration.json and takes over, so you never need to edit this file to
correct an upside-down image.
| Option | Default | Purpose |
|---|---|---|
--port |
/dev/ttyACM0 |
NeoSD serial device |
--art |
./art |
Art folder |
--rotate |
90 |
Starting output rotation (90 or 270 for a portrait panel). Overridden by the admin page's Flip button once used. Only matters for a fresh panel with no saved calibration. |
--idle |
blank |
With no cart link: blank (dark, dies with the cab) or generic (stays lit; for a slot that usually holds a real cartridge) |
--keep-awake |
off | Never sleep the panel automatically. The admin page's Sleep/Wake buttons still work. |
--http-port |
8080 |
Admin and overlay server port |
--manual |
off | No NeoSD Pro on this panel; pick the marquee by hand from the admin page instead. Use for a second panel behind a real cartridge, or any cabinet without a NeoSD Pro at all. See Adding a second marquee. |
--art-source |
none | --manual only. Base URL of another MarqueeMark (e.g. http://marquee.local:8080) to pull art from, so the library lives in one place instead of being copied to every panel. |
--sleep-source |
none | --manual only. URL of another MarqueeMark's /current endpoint. This panel sleeps and wakes to match that cabinet's power state. Omit to control sleep by hand instead. |
--calibrate |
(none) | Advanced: offline terminal calibration for a bench with no network. The admin page is the normal way to calibrate. Keys: arrows move, +/- resize, , . < > tilt, d cycle D-pad orientation, t step size, p pattern/art preview, r reset, s save, q quit. |
- "Connection refused" on the admin page: you skipped the reboot after
installing. Run
sudo reboot. - The web page shows a single marquee image with no controls: that's
the OBS overlay at the bare address. Add
/adminto the URL. - Image is upside down: click Flip 180° in the admin page's calibration controls, then Save.
- Arrows in calibration move th 6DE9 e wrong direction: click D-pad orientation and try again. Which setting is correct depends on this specific panel, not on rotation, so it can differ between two panels even if both are mounted the same way.
- No
/dev/ttyACM0: the cart is slot-powered, the cab must be on. Checkdmesg | tailfor the "NeoSD Virtual Com Port" enumeration. If this panel is in--manualmode, it doesn't use this device at all. - Permission denied on the serial port: your user isn't in
dialout(re-login afterusermod). - Service runs but no journal output:
PYTHONUNBUFFERED=1is missing from the unit. - A game shows the generic or placeholder art: the PNG's name doesn't
match that game's MAME short name. Load the game and read the name
MarqueeMark wants from the journal (
journalctl -u marqueemark -n 5), then rename your file to match and re-upload it. - Art doesn't restore after a power cycle: the slot map has to see
each flash slot announced once. Cycle through your virtual slots one
time to seed it. Also confirm
/opt/marqueemarkis owned by the service user (root-owned files blocklastgame.json). - Panel never sleeps: verify the sudoers rule, and test your driver
board manually:
echo 4 | sudo tee /sys/class/graphics/fb0/blankshould put it into standby (echo 0wakes it). Boards that show a permanent "NO SIGNAL" box instead can't use this feature, run with--keep-awake, and use the admin page's Sleep/Wake buttons by hand instead. - A second panel isn't following the cabinet to sleep: confirm its
service line actually includes
--sleep-sourcepointed at the primary Pi's/currentURL. Runsudo systemctl cat marqueemarkon the second Pi to see the exact command it's running. - A second panel's art list is empty or stale: confirm
--art-sourcepoints at the primary Pi and that Pi is reachable on the network. This panel falls back to its local cache when the primary can't be reached, so a brief outage shows old art rather than nothing.
- The NeoSD protocol here is unofficial and could change in future TerraOnion firmware. Firmware 1.07 behavior is what's documented above.
- A second, manual-mode panel needs its own Raspberry Pi. A single Pi driving two panels at once is not possible under the display driver MarqueeMark currently uses.
Built by Britt at Gamesboro. The NeoSD Pro USB announcement protocol was reverse-engineered on real hardware for this project. Not affiliated with or endorsed by TerraOnion or SNK.
