Using it

Gaming

Edit on GitHub · last edited

SynapseOS runs a 7B model as a system service, which means something has to give when you launch a game — the model pins around 4 GB of VRAM and a pile of worker threads. Game mode (Super+G) is that negotiation, and syn game is the launcher wrapper that goes with it.

These solve different halves of the problem and it is worth knowing which is which:

When it applies What it does
Game mode (Super+G) a game that is already running frees the GPU, holds off idle
syn game at launch loads the FPS overlay, CPU/GPU governor, optional gamescope
syn-arcade any time shows/hides/moves the overlay, sets up controllers, wraps a low-resolution game in gamescope, and opens big screen mode

The overlay has to be loaded at launch — you cannot inject a Vulkan layer into a process that is already up, which is why the launcher exists separately. But once it is loaded, syn-arcade can turn it on, off and move it around from a keypress without leaving the game. See the overlay and controllers.

On a television, start here instead: Big Screen. Super+F10 (or the pad's Guide button) puts up a ten-foot interface — your Steam library with its cover art, media, music and the machine's own switches, all drivable from a controller with the keyboard left on the table.


Fitting an old game to the screen

A game that renders at 640×480 or 1024×768 has no idea what a 2560×1440 monitor is, so it sits as a postage stamp in the middle of a black screen. gamescope fixes that, and the line is hard to remember:

gamescope -w 1024 -h 768 -W 2560 -H 1440 -f -F fsr -- wine Sims.exe

syn-arcade fit writes it, and gives it a menu entry:

syn-arcade fit new --name="The Sims" --exec="wine Sims.exe" --game=1024x768
syn-arcade fit new --from=/usr/share/applications/thegame.desktop
syn-arcade fit                  # the wrappers you have made
syn-arcade fit command <id>     # the command line it builds, to check or copy
syn-arcade fit run <id>         # what the menu entry runs
syn-arcade fit remove <id>      # the wrapper, its menu entry and its desktop icon

--game is what the GAME renders at (gamescope -w/-h); --screen is the MONITOR (-W/-H). The lower-case pair and the upper-case pair are one letter apart and mean opposite things, and getting them the wrong way round produces a wrapper that runs and looks worse than none.

--screen defaults to your primary monitor and --filter to FSR; --force-window, --grab, --overlay and --gamemode are there for the games that need them, and fit inspect <FILE> shows what a wrapper made from an installed application would be without making one.


Steam, and CachyOS Proton

Steam is an installer option, not something the ISO carries: taking it enables [multilib] and brings the 32-bit stack, mangohud, gamemode and gamescope with it.

It also installs proton-cachyos-slr — Valve's experimental Proton branch plus the CachyOS patch set, built against the same Steam Linux Runtime Valve's own Proton uses. It lands in /usr/share/steam/compatibilitytools.d/, which is the directory Steam scans at start, so it appears in a game's Properties → Compatibility dropdown with nothing to copy into ~/.steam.

Valve's Proton stays installed and stays the default. This is an extra option per game, not a replacement — try it when a title misbehaves, leave it alone otherwise.

It comes from CachyOS's repository, which the installer adds on every install (it also carries sunshine, for remote streaming). How that is done matters:

  • Only the generic [cachyos] repo, appended last in /etc/pacman.conf. pacman takes the first repository in the file that carries a package name, so at the end it can only supply names nothing else has — proton-cachyos-*, sunshine. core and extra keep every package they share. SynapseOS stays Arch.
  • CachyOS's own cachyos-repo.sh does the opposite: it inserts the v3/v4 repos above [core] and installs their build of pacman, which re-sources the whole system from CachyOS. Do not run it here — the installer adds the repository by hand for exactly this reason.
  • The signing key is pinned by fingerprint and checked before anything imports it.

Already installed and want it?

sudo pacman -S proton-cachyos-slr      # ~340 MB down, ~1.6 GB installed

proton-cachyos-native is the same Proton outside the runtime container, for driving Proton without Steam. If [cachyos] is not in your pacman.conf — an install from before 1.0 where you declined Steam — synpkg cachyos enable-repo adds it the way the installer does.

Game mode

What triggers it

A fullscreen XWayland client, minus an exclusion list. That is the whole signal, and it works because of what the desktop looks like: Steam titles, Proton/Wine and native X11 games all present as fullscreen X11 clients, while modern desktop apps are Wayland-native and never match.

It is automatic — Super+G toggles the feature, not each game.

What it does while a game is up

  • Releases the AI model. synapd holds the largest single allocation on the card — measured at 7300 MiB with it up against 2655 without — plus the RAM the weights sit in and the cores that answer with them. Game mode asks it to let all of that go, and to load it again on the way out.

    ⚠ Released, not merely made room for. llama.cpp fixes n_gpu_layers when a model is created and has no way to move a layer afterwards, so "offload some to RAM" is really destroy-and-reload at a smaller size — and the layers that come off the card are then resident in system RAM and computed on the CPU. A game wants all three. Freeing VRAM by moving the cost onto RAM and CPU is not handing the machine over.

  • The daemon itself stays up. It is a message (SLEEP on the way in, WAKE on the way out), not systemctl stop. Stopping the unit took the 274 MB retrieval embedder down with the chat model and killed the socket, so chibi's memory went dark for the length of every game; now retrieval keeps working and only the chat model goes away.
  • The synapd poller keeps running, because synapd is still there.
  • Holds off the idle stages. A gamepad is not a seat input device, so to the compositor a controller-only session looks perfectly idle — without this the screen dims and then blanks mid-game.

Everything is restored on exit, including when synui itself shuts down. Nothing reloads a released model on its own — a query while it is asleep is refused rather than queued behind a load — so the wake really is what brings it back, and a compositor that died mid-game would otherwise leave the box with no assistant until the next reboot.

The bar shows an indicator while it is active. synui publishes the state to $XDG_RUNTIME_DIR/synui-game (0700, session-user owned, so nothing else can forge it) and the bar module reads it.

The exclusion list

Fullscreen X11 things that are not games:

firefox · chibi · tepris · nexus-chat · kitty · foot

A fullscreen video is the case this exists for. Without it, going fullscreen on a YouTube video would unload the AI model — a genuinely nasty surprise. The Firefox-app-mode apps report their own app_id via MOZ_APP_REMOTINGNAME, so they need naming separately from firefox.

Add your own with game_exclude in synuirc; matching is case-insensitive substring against the app id.

Settings

Key Default What it does
game_mode on The feature as a whole
game_suspend_ai on Release synapd's model while a game is up
game_inhibit_idle on Don't dim or blank during a game
game_ai_stop_cmd / game_ai_start_cmd (empty) Deprecated. Nothing runs unless set; a box that configured them keeps what it configured
game_exclude see above One entry per line; adds to the defaults

No root any more. Stopping the unit needed a tightly-scoped sudo -n rule (/etc/sudoers.d/synapd-gamemode) because synapd is a system unit and the session has no polkit agent. Asking the daemon over its own socket needs none of that, so a default box does not shell out to sudo at all.


Launching a game: syn game

Wraps a command in gamemoderun (CPU/GPU governor), MangoHud (the overlay), and optionally gamescope (a nesting micro-compositor for clean scaling and a frame cap).

syn game ./game.x86_64
syn game steam                       # one wrap covers a whole library
syn game -- wine some-game.exe
synui-game-run --gamescope=2560x1440@60 -- steam -gamepadui

syn game is the front door and synui-game-run is the same program under its own name — syn game supplies the -- for you.

In Steam, set it as a launch option — the %command% form:

syn game -- %command%

Flags: --gamescope[=WxH[@R]] · --no-hud · --no-gamemode · --ensure-config

Every wrapper is optional and guarded. A missing tool is dropped rather than being fatal, so the command still launches, just without that layer. gamescope ships on the ISO, mangohud arrives with syn-arcade, and lib32-mangohud and gamemode are optdepends:

sudo pacman -S lib32-mangohud gamemode

⚠ The overlay comes from the launcher, and only from the launcher

MANGOHUD=1 is no longer exported for the session, and that is a deliberate change rather than an omission. MangoHud's Vulkan manifest declares enable_environment MANGOHUD=1, so that one variable loads VK_LAYER_MANGOHUD_overlay into every Vulkan client on the machine — a game, a browser, a video player, any Qt application that touches QtMultimedia, a test suite. On AMD the layer segfaults the client inside its own vkCreateDevice hook; on NVIDIA it never does. So it is a crash that follows the graphics card rather than the program, which is exactly the kind that gets blamed on four different applications in turn.

Nothing can inject an overlay into a process that is already running anyway, so the launcher is the only place it could have come from. syn game wraps it, and everything started from that wrap inherits it.

If your machine has never seen the crash and you want the old behaviour:

syn game hud on              # MANGOHUD=1 back in every Vulkan client, at next login
syn game hud off             # …and back to games only
syn game hud                 # which it is now

That writes ~/.config/synapseos/mangohud.conf; the machine's own default lives in /etc/synapseos/mangohud.conf and ships off.

The overlay is hidden until you ask

The overlay is loaded but invisible until something shows it. syn-arcade is what shows it, and it works inside a game that is already running:

syn-arcade hud toggle        # show it or hide it
syn-arcade hud cycle         # move it to the next corner
syn-arcade hud position top-right
syn-arcade hud set font_size 24

Put the first two on keys:

syn-arcade binds install     # Super+F11 toggles, Super+F12 moves it

That writes two ordinary bind = lines into your synuirc, so the Super+/ shortcuts palette can rebind them like anything else. It is not done for you — nothing edits your compositor config until you ask. Pick your own combos with --toggle= and --cycle=, and syn-arcade binds remove takes them back out.

Shift_R+F12 — right Shift, not left — still works as MangoHud's own built-in toggle, handled inside the game process. It is deliberately a different key from the two above: two things toggling one setting on one keypress would cancel each other out.

MANGOHUD=1 only hooks Vulkan. An OpenGL game needs the wrapper: mangohud %command%, or syn game, which does it for you. This is the single most common reason the overlay "doesn't work".

Which config file the overlay actually reads

This trips people up, so it is worth being exact. MangoHud reads exactly one config file. It has a list of candidates and returns on the first that opens — there is no merging, and the list is not in the order you would guess:

~/.config/MangoHud/wine-<exe>.conf     ← checked first
<game directory>/MangoHud.conf
~/.config/MangoHud/<program>.conf
/etc/MangoHud.conf
~/.config/MangoHud/MangoHud.conf       ← checked last

So /etc/MangoHud.conf outranks your own file, and on a stock setup nothing you put in ~/.config/MangoHud/MangoHud.conf was ever read.

SynapseOS pins MANGOHUD_CONFIGFILE to your own file (from /etc/profile.d/syn-arcade.sh), which collapses that whole list to one path you can write. Two commands tell you where you stand:

syn-arcade hud path      # which file is winning, and who outranks it
syn-arcade hud adopt     # take ownership, keeping the settings already in effect

hud adopt copies whatever is currently in effect into your own file first, so taking ownership does not change how the overlay looks.

Edit that file freely — syn-arcade only ever rewrites the individual lines it is asked to change, and keeps everything else, comments included.

Why a keybind can change the overlay in a game that is already running

MangoHud is an implicit Vulkan layer living inside the game's own process. There is no socket and no signal to send it, and mangohudctl does not reach it — that speaks to the separate mangoapp process used by gamescope and SteamOS.

What libMangoHud does do is watch its own config file with inotify and reparse on every change. So rewriting that file is a live control channel into every running game at once, which is what makes a plain compositor keybind possible.

One consequence worth knowing, because it explains an otherwise baffling failure: the config file must exist before the game starts. MangoHud adds its watch once, at layer init, and if the path is missing at that moment the watch fails and that process never sees a config change again — the keys are dead in that game for as long as it runs, while working fine in the next one you launch. Your session runs syn-arcade hud ensure at login so this cannot happen.


GeForce NOW

syn-gfn — no arguments, or the GeForce NOW entry in the applications menu, or its tile in big screen mode.

syn-gfn
syn-gfn --list-browsers      # what it found, and which one it would use

It is a launcher, and the browser is the client, because the Electron client cannot do either of the two things a game stream is — and both failures are silent.

  • Its Wayland branch hardcodes a GL implementation Chromium 142 removed, so the GPU process dies at every launch and falls back to software compositing — in which its Wayland surface never repaints at the size the compositor gave it. synui sends configure(2556, 1382), the client answers set_window_geometry(0, 0, 2556, 1382) and acknowledges it, and then paints 800×600 into the corner with the desktop showing through the rest. A stream negotiated against that viewport comes back 5:4.
  • It binds zwp_pointer_constraints_v1 and never calls lock_pointer, so nothing asks the compositor to hold the cursor and it leaves the window mid-game onto the next monitor.

Neither is synui's doing — a compliant client driven through the same maximise-then-fullscreen sequence in a nested headless synui is configured to the exact output box and fills it, and both pointer protocols are advertised and answered.

Every hard part of a cloud-gaming client — pointer lock, keyboard lock, fullscreen, H.264/HEVC decode, WebRTC — belongs to the browser engine, already written and already tested against this service. So syn-gfn runs the first Chromium-family browser it finds, in a profile of its own (never your browsing one), with keyboard lock, pointer lock and automatic fullscreen pre-granted for the site — the permission prompt they replace is raised while the page is already fullscreen with the cursor captured, where nobody can see it. Keyboard lock is what makes Escape reach the game instead of being spent leaving fullscreen.

No browser is a dependency. GeForce NOW refuses Gecko, so Firefox cannot run it. If --list-browsers comes back empty, install any Chromium-family browser and it will be found.


Controllers

Steam handles its own controller input. This is for everything outside it — emulators, native Linux games, and anything launched from outside a store.

syn-arcade pads                     # what is plugged in
syn-arcade pads info <pad>          # buttons, axes, deadzones, live positions
syn-arcade pads test <pad>          # watch buttons and sticks as you press them
syn-arcade pads rumble <pad>        # check the motors

A <pad> is its event id (event20), its number in the list (2), or any unique part of its name (dualsense). An ambiguous fragment lists the candidates rather than picking one.

syn-arcade gui puts all of this in a window.

Stick drift

syn-arcade pads calibrate <pad>

Measures how far each stick wanders while nobody is touching it, then sets the kernel's per-axis deadzone wide enough to cover it. Let go of both sticks first — a reading taken with one held is refused rather than believed, because believing it would give you a controller that ignores half its own range.

The reason to do it this way: the deadzone lives in the kernel's description of the device, and SDL, evdev-based emulators and most native games read it from there. So one calibration fixes drift everywhere at once, instead of hunting for a deadzone slider in every game that has one.

Other forms:

syn-arcade pads calibrate <pad> --deadzone=10   # skip measuring, set 10%
syn-arcade pads calibrate <pad> --reset         # clear what this set

Deadzones live in the kernel's copy of the device, so they are lost when the pad is unplugged. Save them and your session puts them back at every login:

syn-arcade pads save <pad> --deadzone=10

They are remembered against the controller's USB ids, not its event number — event20 is whatever number happened to be free when you plugged it in.

Buttons in the wrong places

If A and B are swapped, the triggers act like buttons, or the right stick reads as a d-pad, the pad is missing from SDL's mapping database or is in it wrongly. No game has a setting for this, because from the game's side nothing is wrong.

map learn builds one for you — press each control once and it writes the mapping. It exists because the Mappings tab could list a broken controller and not fix one: its answer to "my pad's buttons are in the wrong places" used to be the name of a program this desktop does not ship.

syn-arcade map learn [<pad>]                 # press each control once
syn-arcade map add '<the mapping string>'    # or paste one — quote it
syn-arcade map                               # what you have added
syn-arcade map remove <guid or name>
syn-arcade map path                          # is SDL actually reading them?

A mapping from antimicrox or the SDL project's gamepad tool still pastes in.

Every SDL2 and SDL3 game reads them, through SDL_GAMECONTROLLERCONFIG_FILE.

A mapping that says platform:Windows loads and is then silently never applied — SDL only uses mappings matching the platform it is running on. This is the single most common reason a mapping copied off a forum does nothing, so map add refuses that one and tells you what to change.

It does not need root

Rumble and deadzones write to the controller's device node, which is root:input — and you are not in the input group. It still works, and that is deliberate: udev tags joysticks, and only joysticks, for uaccess, so whoever is logged in at the machine is granted access to them. Keyboards and mice get no such rule, which is rather the point.

So if you see permission denied, sudo is not the fix. It means one of two things: udev did not recognise the device as a joystick (check with udevadm info /dev/input/eventN | grep ID_INPUT), or this session is not the active seat — an SSH login, or another user switched to.


Things that will confuse you

"Fullscreen" is a lie

Some games' fullscreen is borderless windowed: they never send _NET_WM_STATE_FULLSCREEN, so the compositor correctly tiles them, game mode never triggers, and it looks like synui is broken.

Doom KEX is the standing example — v_windowmode=1 is borderless, 2 is true fullscreen. Either fix it in the game, or force it with Super+Shift+F.

A game opens on the wrong monitor

Xwayland with no primary output leaves SDL falling back to connector order. Set one: Super+Dp, or primary=1 in outputs.conf.

A correctly-sized window that renders nothing

Usually the game, not the compositor. Unity titles persist a screen-resolution index rather than a resolution, so moving a game between monitors with different mode lists can leave the saved index out of range — which resolves to a 1×1 viewport inside a properly sized window. Delete or fix the game's prefs entry.

A blank window at the right size is a client resolution bug far more often than a compositor bug.

Windows games

wine and wine-mono ship on the ISO, and there is a Run with Wine action in the file manager's context menu (synui-wine-run).

Two traps worth knowing if you use Lutris or a prefix by hand:

  • A 32-bit game needs arch: win32 in the config, not just a 32-bit prefix.
  • gamescope and Wine do not always mix — if a Wine title misbehaves under --gamescope, run it without.

The AI came back and the network didn't

synnet used to carry a Requires= on synapd, so stopping synapd stopped synnet too — and starting synapd again did not bring it back. It is a Wants= now. The shape is worth remembering: check Requires= before you stop a unit, and remember the dependency only points one way.


See also: synui, synapd, Keybindings, Troubleshooting.