ERGS PLAYER / FIELD MANUAL

From a blank screen
to a living scene.

Define your scene and send your first command. No ERGameStudio installation required.

01 / QUICK START

Your first scene.

You'll need the Player, an MQTT broker reachable from your device, and an MQTT client or game controller. The examples below use mosquitto_pub to send commands.

  1. Install the package for your platform.
  2. Create a project folder, for example C:/ergs/player/ on Windows or /home/pi/ergsplayer/ on Linux. Add a media directory inside it.
  3. Save the configuration below as config.json in your project folder. Change base_path to that folder and mqtt_broker_uri to your broker.
config.json · Windows example
{
  "base_path": "C:/ergs/player/",
  "mqtt_broker_uri": "tcp://192.168.1.10:1883",
  "mqtt_base_topic": "ergs",
  "device_id": "room01",
  "mqtt_client_id": "ergsplayer-room01",
  "resolution": { "width": 1280, "height": 720 },
  "fullscreen": false,
  "cursor_visible": true,
  "exit_by_keyboard": true
}

For Linux, change base_path to /home/pi/ergsplayer/, replacing pi with your actual username. Use a unique device ID and MQTT client ID for every Player.

Save this scene as media/screen.json. It uses the built-in font and needs no external assets.

media/screen.json
{
  "default_scene": "main",
  "scenes": [
    {
      "scene_id": "main",
      "elements": [
        {
          "type": "text",
          "id": "message",
          "pos": [100, 280],
          "size": 48,
          "color": "#C6F46B",
          "value": "Your story starts here."
        }
      ]
    }
  ]
}

Launch with an explicit path to your configuration:

Windows · PowerShell · from the installed player folder
.\ergsplayer.exe C:/ergs/player/config.json
Linux · use ergs-player for the Raspberry Pi package
ergsplayer /home/pi/ergsplayer/config.json

From a machine with an MQTT client installed, send your first update. Replace the broker address with your own.

MQTT · change the text on screen
mosquitto_pub -h 192.168.1.10 -t ergs/room01/update/message -m "The door is now unlocked."

This updates the on-screen message. Connecting the command to a physical lock or puzzle is the responsibility of your game controller.

Download Windows config · Download Linux config · Download the sample scene

02 / CONFIGURATION

The runtime, configured your way.

Pass a configuration file explicitly when launching the Player. See the installation guide for platform-specific launch commands and installed directories. The base_path setting determines where the Player loads your scenes and assets.

SettingPurpose
base_pathProject folder containing media/screen.json and media/assets/. Set it explicitly to keep asset paths predictable.
mqtt_broker_uriBroker URI, for example tcp://192.168.1.10:1883.
mqtt_base_topic / device_idBuild the device namespace: ergs/room01.
mqtt_client_idUnique broker client identifier. Give each running Player its own value.
resolutionStartup dimensions: {"width":1920,"height":1080}.
rotationClockwise virtual rotation: 0, 90, 180 or 270. Quarter turns swap the default logical scene axes.
fullscreenUse fullscreen or a normal window.
cursor_visibleShow or hide the system pointer.
always_on_topRequest a topmost window where the platform supports it.
exit_by_keyboardSet to false to prevent ESC from exiting the Player.
timer_topic / timer_element_idForward an external timer topic to the specified timer element's time command.
game_events_topicForward external game events to elements in the active scene.
terminate_on_endgameExit on the onEndPlay event when enabled.
post_shader_fragmentOptional fullscreen fragment shader. Accepts an absolute path or a path relative to the project or its assets directory.
shader_time_uniformTime uniform passed to the postprocess shader each frame; default time.
ambient_fileLooping background audio filename in media/assets/.
ambient_audio_device / audio_channelsLinux ALSA routing. Map channel names such as sfx or voice to devices; an empty string uses the system default.
03 / SCENES & ASSETS

One project. Multiple scenes.

The Player loads media/screen.json under your configured base_path. Put video, audio, images, fonts and shader files in media/assets/.

Project layout
project/
├── config.json
└── media/
    ├── screen.json
    ├── dialog-example.json
    └── assets/
        ├── intro.mp4
        ├── ambient.ogg
        └── terminal.ttf

Only one scene is active at a time. Set default_scene to its scene_id. For transitions, use top-level "transition": "fade" and "transition_time": 0.5, with optional per-scene transition_in and transition_out durations.

Position, layers and styling

Elements use pos: [x, y], visible and layer (lower layers render behind higher ones). Most visual elements accept size: [width, height]. Text-based elements use a scalar size for font size; do not duplicate the JSON key. Use globally unique element IDs to make routing and discovery unambiguous.

Common framing options include frame_color, frame_thickness, frame_patch9 and frame_padding: [x, y]. Colors accept hex values, including alpha where supported.

Reload without restarting

Publish ON to root/reload to reload the current scene JSON. Or send a JSON filename located in the project's media/ folder. A successful reload refreshes subscriptions and autodiscovery. If the reload fails, the current runtime scene remains active. OFF is ignored.

04 / MQTT REFERENCE

A simple contract for every device.

All examples use root as shorthand for <mqtt_base_topic>/<device_id>. With the quick-start configuration, that is ergs/room01.

Commands → Player

Topic under rootPayload / effect
scene/selectScene ID. Activates that scene.
reloadON or filename.json. Reloads the scene tree.
update/<id>String value. Updates the element.
visible/<id>ON / OFF. Shows or hides the element.
cmd/<id>/<suffix>Element-specific command, such as play, clear or file.
audio/ambientON / OFF. Controls the configured ambient loop.
audio/<channel>/playAudio filename from assets. OFF / STOP stops tracked Windows channel sounds; Linux one-shots are fire-and-forget.
audio/<channel>/volume0..100. Linux volume control uses amixer.
shader/uniform/<name>Numeric value or {"type":"float","value":0.65} for the fullscreen shader.

For scene-scoped updates, insert the scene ID: root/update/main/message, root/visible/main/message or root/cmd/main/intro_video/play. Unscoped commands reach all matching element IDs across all scenes. A scoped update targets the named scene, even when it is inactive.

Scene selection also accepts root/cmd/scene/select and root/cmd/__scene__/activate. Shader uniform aliases are root/cmd/shader/uniform/<name> and root/cmd/__shader__/uniform/<name>.

Player → status & events

Topic under rootPayload
status/playeronline / offline, retained. Offline is also the Last Will.
status/scene/currentActive scene ID, retained.
status/scene/event{"event":"changed","from":"old","to":"new"}
status/reloadJSON result with state, file and scene.
input/<id>Submitted text. Also available at status/<id>/input.
status/<id>/clickButton: ON on press, OFF on release. Button grid: clicked label.
status/<id>Keypress and GPIO input changes: ON / OFF.
status/<id>/eventVideo / audio visualizer lifecycle: on_play, on_stop, on_finish.
status/<id>/stateMedia state: playing, stopped, finished, retained.
status/audio/<channel>/eventChannel playback lifecycle events.
status/audio/<channel>/fileLast started filename, retained.
status/audio/<channel>/volumeLast requested volume, retained.
status/audio/ambient/stateAmbient playback state, retained.
pingON every 20 seconds.

Lifecycle events are non-retained pulses; current-state topics are retained. Avoid retaining transient control commands that should not replay when a device reconnects.

Autodiscovery

At startup and after successful reloads, the Player publishes a retained description to <mqtt_base_topic>/autodiscovery/player_<device_id>. It includes the root topic, scenes, elements and typed MQTT input/output fields. Duplicate element IDs are listed in warnings.duplicate_element_ids.

MQTT · watch device status
mosquitto_sub -h 192.168.1.10 -t 'ergs/room01/status/#' -v
05 / SCENE ELEMENTS

Your building blocks.

Mix visual elements with input and output. Each element has a type and a unique id.

text

Dynamic TTF text, multiline wrapping and live MQTT updates. Send a new string through update/<id>.

button

Mouse and touchscreen buttons publish ON/OFF on status/<id>/click. Use on_press and on_release for local action chains.

image

Static PNG/JPG textures. Combine position, size and layers to build backgrounds, clues and interface graphics.

video

VLC media playback, loops and Linux V4L2 camera feeds. Commands: play (ON/OFF) and file (filename). Use audio: false for silent video.

timer

A text-based game timer that parses game-timer JSON. Connect an external timer_topic to the chosen timer_element_id.

progressbar

Horizontal fill bar driven by a percentage value. Update the element value over MQTT to reflect game progress.

round_progress

Circular HUD-style progress ring with center text and subtext. Drive the percentage through element value updates.

button_grid

A matrix of buttons. Grid labels use pipe-separated strings such as 1|2|3|4. The clicked label is published to status/<id>/click.

input

Single-line keyboard input. Submitted text is published through input/<id> and status/<id>/input.

keypress

Non-visual keyboard controls on Windows and Linux. Key names accept F12, KEY_SPACE or A. Publishes ON/OFF to status/<id>.

chat

Rolling message log. Prefix a message with {#FF4444} for a custom color. Commands include clear, maxlines and color.

frame

Decorative borders and textured frames. Style with frame_color, frame_thickness, frame_patch9 and frame_padding.

shader

Procedural fragment-shader surfaces. Send a JSON uniform map through update/<id>, or use cmd/<id>/uniform/<name>.

audiovisualiser

Audio playback with waveform, circle or particles modes. Supports MP3, WAV and OGG through the raylib audio backend.

gpio

Raspberry Pi physical input and output using BCM numbering. Modes: input, input_pullup and output. See the GPIO examples below.

interactive_dialog

Branching interviews using an existing video or audio visualizer and button, keypress or GPIO inputs. Stays hidden while idle. See the action and dialog guide below.

Example: a video with a local action

Element inside a scene's elements array
{
  "type": "video",
  "id": "hint_video",
  "file": "hint01.mp4",
  "loop": false,
  "audio": true,
  "layer": 0,
  "on_finish": "hint_video.hide;message.show"
}

Place hint01.mp4 in the assets directory. The message element must exist in the project. For a Linux camera stream, use "file": "/dev/video0".

Audio-reactive scenes

Active audio visualizers can feed shader uniforms audioAmplitude, audioProgress and audioActive. Visualizer commands include play, file, volume and mode. Analysis availability depends on the platform and media backend.

06 / ACTIONS & DIALOGS

Small interactions. Bigger stories.

Use semicolon-separated action chains in event hooks. They run from left to right. Buttons, keypress elements and input-mode GPIO support on_press and on_release. Video supports on_play, on_stop and on_finish.

Action chain
main.switchto;message.set("Access granted");message.show;intro_video.stop

Supported actions include .play, .stop, .switchto, .show, .hide, .toggle, .set("text") and .open('filename'). The open action loads and starts a dialog, video or audio visualizer.

Interactive dialogs

A dialog references an existing media element using media_element_id and input elements using choice_input_ids. Input types can be mixed. Provide cancel_input_id and timeout_seconds as needed.

Dialog element · referenced IDs must exist
{
  "type": "interactive_dialog",
  "id": "interview",
  "pos": [80, 400],
  "size": [1120, 280],
  "media_element_id": "dialog_audio",
  "choice_input_ids": ["choice_a", "choice_b"],
  "timeout_seconds": 60,
  "content_font_size": 28,
  "choice_font_size": 30
}

Publish a metadata filename to root/cmd/interview/file to start or replace the dialog. Use root/cmd/interview/play with ON/OFF, or root/cmd/interview/close to close it.

Metadata uses intro_text, intro_audio and options. Options support id, label, question, answer, question_audio, answer_audio, important, repeatable, show_after and hide_after. Audio asset paths are relative to media/assets/.

The dialog ends when all important options have been asked, no options remain, cancel is pressed or the choice timeout expires. Set a file and autoplay: true to open the dialog whenever its scene becomes active.

07 / GPIO & RASPBERRY PI

Connect your scene to the room.

For package installation, file locations and launching without a desktop, see the Raspberry Pi installation guide.

GPIO is available in Raspberry Pi builds and uses BCM numbering. The Debian 13 build uses the libgpiod v2 API. Inputs publish state over MQTT; outputs respond to commands from your game controller.

Example GPIO elements
{
  "type": "gpio",
  "id": "door_sensor",
  "pin": 17,
  "mode": "input_pullup",
  "invert": true
}

{
  "type": "gpio",
  "id": "relay",
  "pin": 27,
  "mode": "output",
  "initial": "OFF"
}

Add these as separate objects to the scene's elements array. The sensor publishes ON/OFF to root/status/door_sensor. Control the output with root/update/relay. invert reverses logical and physical levels.

The download targets arm64, not every historical Pi model. Check architecture, drivers and your scene's performance on the actual device. No universal boot-time or frame-rate guarantee is implied.

08 / LICENSING & THE STUDIO

The Player, on its own terms.

ERGS Player is not an open-source release. It follows a Freemium / OpenCore model. The license included with each distribution defines usage rights and the features available in that release. This guide does not establish pricing or a feature entitlement matrix.

ERGameStudio's public release is planned for late 2026. It will bring a WYSIWYG scene editor, node-based game logic, autodiscovery and remote upload/update workflows. Until then, use the standalone Player with JSON scenes and your MQTT controller.

Get ERGS Player ↓