Making a Recalbox theme

45 pages · 8 sections
FRENESDE Show everything Create my theme

Using variables

Displaying a game's name, picking an image per system.

A variable is written ${…} and Recalbox replaces it at display time.

<text name="titre">
  <text>Bienvenue sur ${system}</text>
</text>

→ “Bienvenue sur Super Nintendo”.

In an image path: the most useful

<image name="console" extra="true">
  <path>${root}/data/arts/consoles/${system.name}.png</path>
</image>

One single line, and each system displays its own image. The files just have to bear the system's internal name: snes.png, megadrive.png

Where each variable works

This is the rule that surprises the most, and it comes from the engine:

FamilyViews where it is resolved
${system…}Systems and Game list — a list always belongs to a system
${game…}Game list and Screensaver — wherever there is a hovered game
${recalbox…} ${settings…} ${hardware…} ${display…}everywhere

A variable used where it does not exist is not replaced: the raw text shows as is, ${game.name} included. The studio only offers the ones that work on the current view.

When the highlighted row is not a game

In the game list, the cursor does not only travel across games: it also lands on folders and on sort headers, those intertitles Recalbox adds as soon as the list is sorted by anything other than alphabetical order. The ${game…} variables still answer, but they then describe a row that has no game:

Highlighted row${game.name}${game.releasedate}${game.file.name}
a gameits nameits release datethe rom file
a folderthe folder nameUNKNOWNthe folder name
a sort headernothingUNKNOWNnothing

A component showing game data therefore has nothing left to say on those rows — and yet it stays on screen, on top of the folder name Recalbox writes at the same moment. Restrict it to game rows:

<text name="sortie" extra="true" showIf="game">
  <text>Released: ${game.releasedate}</text>
</text>

The studio takes care of it: as soon as a component uses game data, its visibility switches to “a game”. The component’s Visibility tab lets you open it back up to folders and headers whenever that is what you want.

${root} — never to forget

${root} designates the root of the selected theme. Without it, paths are relative to the file that writes them, and your theme breaks as soon as it is stored differently.

<path>${root}/data/arts/fond.jpg</path>     <!-- ✅ -->
<path>../data/arts/fond.jpg</path>          <!-- fragile -->

The old $… variables

In old themes you will come across variables without braces:

OldWhat it givesCurrent equivalent
$systemthe short name — “snes”${system.name}
$themethe theme's folder${root}

⚠️ $system and ${system} do not give the same thing: the first yields “snes”, the second “Super Nintendo”. They are still accepted, but deprecated: only write the braced form from now on.

The random draw

<path>${random.between(fond1.jpg,fond2.jpg,fond3.jpg)}</path>
<fontSize>${random.range(1,10)}</fontSize>

random.between picks a value at random from the list, random.range a number between two bounds. The draw happens when the theme loads, not on every display.