Docs menu

Docs ▸ Steam

Steam

Your game gets window.steam in desktop builds and in play. It works with your own copy of Valve's Steamworks SDK; Gameshipper never ships Valve's files itself.

  1. Download the Steamworks SDK zip from https://partner.steamgames.com/downloads/list (you need a Steamworks partner login).
  2. Put it in your game folder's .sdk/ folder, e.g. .sdk/steamworks_sdk_165.zip. init adds .sdk/ to .gitignore: don't commit Valve's files. You can also set steam.sdk in gameshipper.json (relative to the game folder), set GAMESHIPPER_STEAM_SDK, or pass --steam-sdk <path>. Once a build has used an SDK, Gameshipper keeps its libraries in its cache and later projects find them there. The SDK is never copied into the game, whatever it's called and wherever it is.
  3. npx gameshipper status shows which SDK builds will use. Without one, builds still work (with a STEAM_SDK_MISSING warning) and window.steam.isAvailable() resolves false.

Each build gets only the Steam library its OS needs, copied from your SDK. Nobody needs a steam_appid.txt, and none is ever shipped.

if (window.steam && (await steam.isAvailable())) {
  await steam.unlockAchievement('FIRST_WIN');
  await steam.setStat('WINS', 10);                 // saved to Steam right away
  await steam.uploadLeaderboardScore('HIGH_SCORES', 12345);
  const top = await steam.getLeaderboardEntries('HIGH_SCORES', 10);
  await steam.setRichPresence('status', 'Level 3');
  const name = await steam.getUserName();
  steam.activateOverlay('achievements');
}

Every method resolves to a harmless value (false, null or []) when Steam isn't running, so the game never needs to guard each call. Also available: getSteamId, getLanguage, isSteamDeck, clearAchievement, isAchievementUnlocked, listAchievements, getStat, storeStats, clearRichPresence, activateOverlayWebPage, and GemShell-style names (getPersonaName, getSteamID, getAchievement, getStatInt, setStatInt, getStatFloat, setStatFloat, requestStats, requestStatsAndWait, runCallbacks), so GemShell games need few or no changes. Only the game's own pages can use window.steam. window.steam and window.gameshipper are ordinary globals: a game that has its own steam or gameshipper (a top-level const, let, var, function or class in a classic script, or window.steam = ...) keeps working, and its own takes the place of Gameshipper's.

  • App ID. steam.appId defaults to 480, Valve's Spacewar test app, which every Steam account can use. With your own App ID, a packaged build started outside Steam asks Steam to launch it (Valve's standard check).
  • Overlay (Shift+Tab). steam.overlay is "auto" by default. On macOS that uses a native window that mirrors the game so Steam's overlay can draw on it; on Windows and Linux it keeps a new frame coming 60 times a second. On macOS and Linux this happens only when Steam launched the game, which is when Steam injects its overlay. Set "native", "repaint" or "off" to override. play --smoke warns STEAM_OVERLAY_FAILED when the native window couldn't start.
  • Steam Deck. Steam's on-screen keyboard opens when a text field gets focus, and again when the player taps the field after closing it. Read-only fields get no keyboard.
  • Quitting. The game waits up to a second for Steam calls still in flight (a leaderboard upload, say) before it disconnects from Steam.
  • Troubleshooting. play --smoke --json reports STEAM_NOT_RUNNING (a warning: start Steam) or STEAM_INIT_FAILED (an error: usually an App ID the signed-in account doesn't own).