agentsclimarketplace

Batocera display

Skill t3chnaztea/batocera-skills/skills/batocera-display

Agent skills that teach coding agents to operate a Batocera arcade cabinet: ops, ROMs, display, tuning, maintenance. Claude Code plugin.

Install
npx -y skills add t3chnaztea/batocera-skills --skill batocera-display

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 4 stars4 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Use when working on how games look on a Batocera cabinet: CRT and other shaders, shaderset resolution and per-game shader overrides, bezel/overlay artwork and alignment, generating missing .info sidecars, filtering unusable bezels, pinning the display resolution, or diagnosing moire and cropped/misaligned game viewports inside bezels. Covers why a shaderset seems invalid, why .info top values are not alpha coordinates, and why Mega-Bezel per-game art fails. Not for performance/framerate tuning (batocera-tuning).

SKILL.md

6.8 KB, as published. Nobody here has run it

Batocera Display

Shaders, bezels, and display mode — everything about how the picture looks. Assumes the connection pattern, batocera.conf/configgen model, and verify loop from batocera-ops. Because display changes are visual, the batocera-ops remote verify loop (ES HTTP API launch + batocera-screenshot) is your primary tool here: never declare a shader or bezel "working" from the config alone — launch it and look.

Shaders

Shaders are assigned per-system (or per-game) in batocera.conf. Two key mechanisms and the traps around each.

shaderset resolves against TWO search paths

<system>.shaderset=<name> is a folder-name lookup resolved against both:

  1. /userdata/shaders/configs/<name>/ (user-installed)
  2. /usr/share/batocera/shaders/configs/<name>/ (system-bundled)

Checking only one path is enough to wrongly declare a preset invalid. Some built-in names are aliases that resolve to a preset deep under /usr/share/batocera/shaders/. Before concluding a shaderset value is dead:

SSHB "find /userdata/shaders /usr/share/batocera/shaders -maxdepth 3 -iname '<name>*'"

And read the launch log for the shader actually used on the last launch — it is authoritative:

SSHB "grep -i 'set-shader\|shader' /userdata/system/logs/es_launch_stdout.log | tail"

Two shader keys: shaderset vs -renderer.shader

  • <system>.shaderset=<name> — a Batocera shaderset folder (may bundle bezel art, e.g. Koko-AIO/Duimon presets).
  • <system>-renderer.shader=<path> — a direct RetroArch .slangp path (relative to the shaders dir, or absolute). This is how you assign a plain CRT preset like crt/crt-super-xbr or a custom .slangp.

Per-game overrides use the exact-filename key from batocera-ops → batocera-conf: snes["Super Mario World (USA).sfc"]-renderer.shader=.... A glob (snes["Star Fox*"]...) never matches.

Anti-moiré: integer scaling is impossible inside a custom-viewport bezel

A CRT mask shader (crt-royale and friends) can produce rainbow moiré when the game isn't integer-scaled. The usual cure — video_scale_integeris ignored for a custom viewport, and bezels that force a custom RetroArch viewport (with fractional coordinates) put you exactly there. So you cannot integer-scale inside those bezels.

The workaround is to tame the mask instead of the scale: a wrapper .slangp that #references the CRT preset and sets mask_sample_mode_desired=1 (a scale-tolerant mask sampling mode). Point the arcade systems' -renderer.shader at the wrapper. Verify with before/after screenshots — this is precisely the kind of change that must be seen, not assumed. Full detail in references/shaders.md.

Pin the display mode in batocera.conf, not the boot conf

If a CEC-controlled TV renegotiates EDID on each wake and sometimes lands on 59.94Hz/1080i, pin it: es.resolution=1920x1080.60.00. This must go in /userdata/system/batocera.conf, not /boot/batocera-boot.conf (the boot conf's lower section is auto-synced from batocera.conf on boot and a direct edit gets blanked). Confirm in display.log after a reboot.

Bezels

Bezels/overlays are PNGs with a transparent "cutout" where the game shows, plus a .info sidecar describing the viewport. Packs live under /userdata/decorations/<pack>/, with per-game art at .../games/<system>/<rom>.png (+ .png.info).

.info coordinates are artistic intent, NOT the alpha cutout

The single biggest bezel trap. When a game renders misaligned inside the cutout, the fix is the .info sidecar's top/left/bottom/right — but top is not the PNG's transparent-pixel offset. It's where the artist intends the game's viewport edge to sit, often above the alpha cutout because the top bezel artwork is semi-transparent and the game is meant to extend behind it.

Do not derive .info top from the alpha bounding box (autodetecting the transparent region and using that as top moves the game the wrong way). Instead: launch, screenshot, and nudge top in ~20px steps (smaller = game moves up, larger = down). Back up the .info before each iteration. See references/bezels.md.

Generating missing .info sidecars

Some packs (notably thebezelproject) ship thousands of PNGs without .info files. Without a sidecar, configgen guesses a viewport heuristically and often gets it wrong (undersized, top-anchored game). scripts/generate_tbp_info.py alpha-detects each PNG's cutout and writes a correct .info, never overwriting an existing one. This is the correct use of alpha detection — sizing a missing sidecar — as opposed to the misalignment fix above, which is artistic and eyeballed.

Filtering unusable bezels

Some bezels have a cutout so small the game is postage-stamped. scripts/filter_small_bezels.py moves any bezel whose cutout's shorter dimension is below a threshold into a filtered_too_small/ sidecar dir, so Batocera falls back to the pack default. Fully reversible (mv filtered_too_small/* .).

SNES (and similar) fallback strategy

When a pack has per-game art for only a fraction of a large library (e.g. ~50 of ~760 SNES ROMs) and everything else falls back to an ugly generic system bezel, invert the default: set the system to a clean shader-frame look (a Koko-AIO ambilight preset, <system>.bezel=none) and write per-game overrides only for the ROMs that do have real art. scripts/snes-bezel-fallback.py is a ready-to-adapt implementation (idempotent, generates a marker block, backs up batocera.conf).

Don't attempt per-game Mega-Bezel cabinet art

Combining Mega-Bezel's photoreal CRT with per-game cabinet-art bezels is a known dead end on Batocera (stale bezel symlink returns bezel: None, rotation isn't propagated to the shader, and per-game cutout variance can't be bracketed by one global scale offset). The full postmortem — including the false-positive that fools you into thinking it works after one ROM — is in references/bezels.md. Read it before spending a day on this; the short version is don't.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.