No description
  • C++ 86.2%
  • Python 9.1%
  • TypeScript 4%
  • Shell 0.7%
Find a file
2026-08-17 02:53:24 +02:00
.opencode Profiles 2026-08-17 02:53:24 +02:00
.vscode Initial 2026-08-07 00:13:19 +02:00
assets Added support for static assets 2022-05-25 21:34:31 +02:00
defaults Add defaults folder to template 2022-07-18 16:15:20 -07:00
out Gitignore, build scripts preparation 2026-08-07 02:03:04 +02:00
py_modules Add support for the 'decky_plugin' module exposed by decky-loader (#16) 2023-02-19 14:43:09 -08:00
src Profiles 2026-08-17 02:53:24 +02:00
.gitignore Gitignore, build scripts preparation 2026-08-07 02:03:04 +02:00
AGENTS.md Profiles 2026-08-17 02:53:24 +02:00
decky.pyi support new APIs fully 2024-06-13 18:31:08 -04:00
LICENSE Update LICENSE 2024-02-12 18:12:48 -08:00
main.py Profiles 2026-08-17 02:53:24 +02:00
package.json Profiles 2026-08-17 02:53:24 +02:00
plugin.json Initial 2026-08-07 00:13:19 +02:00
pnpm-lock.yaml update for new react, also support linux aarch64 builds 2025-11-20 00:38:21 -05:00
pnpm-workspace.yaml Initial 2026-08-07 00:13:19 +02:00
README.md Profiles 2026-08-17 02:53:24 +02:00
rollup.config.js use @decky/rollup rollup config template 2024-07-08 20:28:57 -04:00
tsconfig.json update for new react, also support linux aarch64 builds 2025-11-20 00:38:21 -05:00

DeckGrade

A DeckyLoader plugin for the Steam Deck that lets you tune the display's saturation, contrast, and vibrance from the Quick Access menu. More display controls are planned.

Features

  • Saturation (0200%, 100 = neutral) — boosts or drains color intensity; 0% is full grayscale.
  • Contrast (0200%, 100 = neutral) — raises or lowers contrast around the midtones.
  • Shadows (-100100%, 0 = neutral) — brightens or deepens dark areas, with little effect on bright ones.
  • Highlights (-100100%, 0 = neutral) — brightens or tames bright areas, with little effect on dark ones.
  • Vibrance (0100%) — drives the native gamescope color vibrance engine, the same one behind the built-in SteamOS Color Vibrance setting.
  • Per-game profiles — the running game (detected by its Steam AppID) gets its own profile independent of the global one. When a game without a profile starts, the global profile applies as a fallback. Game profiles can be reset to neutral defaults or re-synced to the current global profile with one tap.
  • Master enable toggle, one-tap reset of the active profile to defaults.
  • Settings persist across reboots and are reapplied automatically; a background watchdog restores them if gamescope restarts or another component overwrites the values.
  • Disabling the plugin or uninstalling it restores default display output.

Usage

  1. Open the Decky menu from the Quick Access button (...) in Game Mode.
  2. Select DeckGrade.
  3. Make sure Enable display tuning is on, then adjust the sliders — changes apply live.
  4. While a game is running, switch to its profile to save a game-specific look: it is remembered by the game and re-applied automatically the next time that game is running.

Notes:

  • DeckGrade only works in Game Mode, where the gamescope compositor is running. In Desktop Mode the panel shows a notice; settings changed there are saved and applied automatically once you return to Game Mode.
  • The Vibrance slider writes the same property as SteamOS's own Color Vibrance setting. While DeckGrade is enabled, its value is kept authoritative; if you want to use the system setting instead, disable DeckGrade first.
  • Saturation and contrast currently affect SDR content only (HDR/PQ content is left untouched by design).
  • Game profiles only work in Game Mode (the active-game AppID comes from the gamescope compositor, which is only running there). In Desktop Mode the global profile always applies.

How it works

SteamOS renders everything through the gamescope compositor. Even though the session is Wayland-based, gamescope hosts an XWayland server and reads control values from its X11 root window properties (the same channel Steam itself uses). DeckGrade's Python backend talks to gamescope through those properties via xprop — no compiled binaries, no root required.

Control Mechanism
Saturation Baked into a generated 3D LUT (.cube) and loaded by gamescope via the GAMESCOPE_COLOR_LOOK_G22 "look" property. Applied at scanout time with no latency cost.
Contrast Same generated 3D LUT as saturation.
Shadows / Highlights Same generated 3D LUT (endpoint-preserving tone curves).
Vibrance Native gamescope GAMESCOPE_COLOR_SDR_GAMUT_WIDENESS property.

Per-game profiles. The backend reads gamescope's GAMESCOPE_FOCUSED_APP_GFX root property — the base-layer AppID of the focused game (base-layer AppIDs are only exposed in Steam Mode, which is fine: that is the only mode DeckGrade applies to). The Steam dashboard itself has the fixed AppID 769, which DeckGrade treats as "no game", so the global profile stays active on the dashboard. A game's profile is created the first time you edit or reset it, and its values are stored under that AppID in the settings file.

Requirements

  • Steam Deck with SteamOS 3.5+ (gamescope ≥ 3.12) and DeckyLoader installed.
  • xprop (ships with SteamOS).

Installing

Download the latest release zip and install it through Decky (Install from URL), or extract it to /home/deck/homebrew/plugins/ so you end up with /home/deck/homebrew/plugins/DeckGrade/ and restart the loader:

sudo systemctl restart plugin_loader

Expected zip layout (only needed if packaging by hand):

DeckGrade/
├── dist/index.js
├── main.py
├── package.json
├── plugin.json
├── LICENSE
└── README.md

Troubleshooting

  • Plugin log: /home/deck/homebrew/logs/DeckGrade/plugin.log (detection probes, applied values, xprop errors).

  • Loader log: sudo journalctl -u plugin_loader (or journalctl --user -u plugin_loader for user-service installs).

  • Manual check from SSH while in Game Mode — each should list GAMESCOPE_* properties on the display gamescope hosts:

    xprop -display :0 -root | grep -m3 GAMESCOPE_
    xprop -display :1 -root | grep -m3 GAMESCOPE_
    

Building (development)

Requirements: Node.js v16.14+, pnpm v9 (sudo npm i -g pnpm@9). On Windows, WSL2 with the same toolchain works.

pnpm i
pnpm run build

The frontend is bundled to dist/index.js. The Python backend (main.py) needs no build step; python3 -m py_compile main.py is a quick sanity check. See AGENTS.md for the full development workflow (packaging, deployment, testing).

License

BSD-3-Clause, see LICENSE. Based on the decky-plugin-template.