METABYTE
Back to articles

How to Ship a Steam Game from a Web Studio Background

A practical, engineering-first guide for web teams shipping their first Steam game: toolchain, CI/CD, Steamworks integration, networking, pricing, and production pitfalls.

16 mai 202615 min readAI research draft
How to Ship a Steam Game from a Web Studio Background

Ship a Steam game wrong and you’ll burn months wrestling with builds, depots, and broken controllers instead of collecting wishlists and reviews. Done right, your web-team discipline — CI, observability, and fast iteration — becomes an unfair advantage.

If you’re a web studio shipping your first Steam title, the shortest path is: pick an engine with mature PC tooling, set up a Windows build pipeline, integrate core Steamworks features (achievements, cloud saves, input), test under Proton, and automate uploads with SteamPipe. Keep multiplayer simple (Steam Networking Sockets if P2P is fine), and budget time for store page, playtest, and launch ops.

From web pipelines to Steam pipelines

Web stacks push to a server; games push to a runtime you don’t control. Steam adds a store, auto-updates, branch management, and platform services. The mental model shift is shipping immutable client builds to millions of heterogeneous PCs. Here’s a pragmatic baseline.

Minimum viable toolchain

  • Engine: Unity, Unreal, or Godot (pick one with the ecosystem you can hire for). For many web teams, Unity hits the middle ground of docs, plugins, and asset store content.
  • IDE and build: Visual Studio 2022 for C#/C++, .NET SDK (for Unity editor tools), and MSVC toolchain.
  • Steamworks SDK + SteamPipe (via SteamCMD) for uploading depots.
  • Source control + CI: GitHub Actions or GitLab CI; one Windows runner is essential for building and packaging.
  • Crash + telemetry: Sentry, Backtrace, or Crashpad with a thin analytics layer (respect privacy and provide an opt-out in settings).
  • Packaging: 64-bit Windows build is the default. Optionally a native Linux build; otherwise rely on Proton compatibility.

Architecture at a glance (single-player or small co-op)

  • Client (Unity/Unreal): game logic, save system, Steamworks integration (achievements, overlay, Steam Input).
  • Optional backend (Node/Go/Python): leaderboards, user-generated content moderation, matchmaking fallback, patch notes API. Cache with Redis, store with Postgres or DynamoDB. Front with Cloudflare.
  • Steam platform: distribution (depots/branches), cloud saves, achievements, leaderboards, networking sockets, anti-cheat options.
  • CI/CD: GitHub Actions builds per commit, produce versioned artifacts, run automated smoke tests (headless where possible), then SteamPipe upload to beta branch. Manual promote to default branch for release.

Data flow:

  • Developer merges to main -> CI builds Windows (and Linux) -> smoke tests (startup, controller detection, basic scene load) -> SteamCMD uploads app + depots to beta -> internal QA and playtest -> promote to public.

Building and uploading with SteamPipe

Steam distribution revolves around apps, depots (OS/architecture slices), and branches (public, beta, staging). A minimal setup:

  • App: your game’s Steam AppID.
  • Depots: 1 for Windows (x64), 1 for Linux (x64). Separate a DLC depot per DLC. Keep high-churn content (e.g., user levels) in its own depot to reduce patch size.
  • Branches: public, beta, staging. Use password-protected branches for external QA.

A simple SteamPipe build script using SteamCMD in CI:

#!/usr/bin/env bash
set -euo pipefail

# Inputs: STEAM_USERNAME, STEAM_PASSWORD, STEAM_GUARD (if needed), APP_BUILD_VDF, BUILD_DIR
# BUILD_DIR contains compiled binaries and content by depot folder.

steamcmd +login "$STEAM_USERNAME" "$STEAM_PASSWORD" "$STEAM_GUARD" \
  +run_app_build_http "$APP_BUILD_VDF" \
  +quit

Your app_build_XXXX.vdf references depot VDF files and content paths. Example depot snippet:

"DepotBuildConfig"
{
  "DepotID"    "123456"  // Windows depot id
  "ContentRoot" "C:\\agent\\_work\\game\\Build_Windows64"
  "FileMapping"
  {
    "LocalPath" "*"
    "DepotPath" "."
    "recursive" "1"
  }
  "FileExclusion" "*.pdb"
}

Two CI tips from a web mindset:

  • Deterministic builds: pin engine version in a lockfile and bake it into the build image. Cache shader compilation where possible.
  • Versioning: inject a semantic version and Git SHA into the about screen and crash reports. Make it visible on the main menu to reduce QA confusion.
// Unity example: set version string at build time via scripting define symbols
public static class BuildInfo {
    public const string Version = "1.2.3"; // replaced by CI
    public const string Commit = "abc1234"; // replaced by CI
}

Integrating Steamworks: the short list that matters

Steamworks can do a lot. For a first ship, keep scope tight and production-focused.

Achievements and stats

  • Plan 10–30 achievements. Over-index on early-game triggers to encourage reviews.
  • Use Steam stats to track completion funnels (first win, first boss). This feeds patch notes and tuning.
// Steamworks.NET example: unlock an achievement
using Steamworks;

public class SteamAchievements : MonoBehaviour {
    void Start() {
        if (!SteamManager.Initialized) return;
    }

    public void Unlock(string apiName) {
        if (!SteamManager.Initialized) return;
        SteamUserStats.SetAchievement(apiName);
        SteamUserStats.StoreStats();
    }
}

Steam Input

  • Support Xbox, DualShock, Switch Pro, and generic controllers via Steam Input API. Map common actions and let the overlay handle per-controller glyphs.
  • Test edge cases: hot-plug mid-game, rebinding, simultaneous KB+controller.

Cloud saves

  • Prefer Steam Cloud if files are <100MB per user. Define file patterns in the partner site or via cloud_paths.vdf.
  • Store saves under a stable per-user directory, e.g., Unity’s Application.persistentDataPath.

Leaderboards

  • Use Steam leaderboards if you don’t need cross-platform parity today. If future cross-platform is likely, abstract behind your own backend and mirror to Steam.

Multiplayer networking

For small co-op, Steam Networking Sockets (P2P) is the fastest route: NAT traversal, encryption, and identity are included. For competitive or cheat-sensitive games, plan an authoritative server.

OptionLatencyNAT traversalCostAnti-cheat fitScaleNotes
Steam Networking Sockets (P2P)LowYes$MediumSmall–MediumEasiest for 2–8 player co-op, rely on host authority
Steam Networking Sockets (dedicated)MediumN/A$$HighMediumUse same API with server build; better authority
Custom WebSockets (Node/Go)MediumVia TURN$$MediumMediumFriendly to web talent; implement reliability & NAT yourself
UDP + ENet/Netcode libsLowNo$$HighHighBest perf; higher engineering burden

If you’re weighing engines from a web team’s point of view, we break it down further in our piece on Unity vs Unreal for indie studios. For browser heritage and 2D/3D hybrid tooling, see our browser engines comparison.

Platform specifics that catch web teams

OS builds, Proton, and redistributables

  • Windows x64 is mandatory. Bundle required VC++ Redistributables or use the Steamworks “Prerequisites” installer section to auto-install.
  • Linux: proton compatibility is good for many Unity/Unreal titles. Test using Proton GE and stable. If you do ship native Linux, set a separate depot and test on Ubuntu and Arch-based distros.
  • macOS: Steam still supports macOS, but Metal-only and code-signing/notarization are a rabbit hole. Consider deferring unless you have team macOS experience.

File system and saves

  • Don’t write to Program Files or C:\ roots. Use per-user writable paths (AppData/LocalLow on Windows; XDG paths on Linux). Avoid Unicode/path-length issues by normalizing filenames and using ASCII for critical save names.

Anti-cheat

  • For PvP, integrate EAC or BattlEye with Steam’s documented flow. Validate Proton support if you need Linux. Cheating risk dictates your netcode and backend authority level.

Controllers and DPI

  • Test 125%/150% DPI scaling on Windows; misconfigured UI canvases will blur or clip.
  • Controllers: handle plug/unplug events, multiple controllers, and overlay prompts.

Localization

  • Steam supports store page localization out-of-the-box. In-game, use gettext/CSV/externalized JSON; avoid hardcoded strings. Validate RTL and CJK fonts.

CI/CD: turning web habits into game velocity

  • Branch strategy: main for integration, release/x.y frozen before launch. Tag builds with x.y.z. Map CI tags to Steam branches.
  • Smoke tests: a headless boot that loads a known scene, spawns a basic controller, and exits with 0 if successful. This catches broken assets early.
  • Artifact hygiene: split depots by volatility to reduce patch sizes. Don’t bloat the Windows depot with test content.
  • Secrets: create a dedicated Steam “build” account with limited permissions and its own 2FA. Store its login token in CI secrets. Rotate regularly.
  • Rollbacks: keep the last 3 stable builds available as beta branches (rollback-1, rollback-2). Steam’s branch swap is near-instant for users.

Marketing and the store loop (brief, but critical)

  • Steam Direct: $100 per app recoupable after $1,000 net revenue.
  • Store page: set up early (3–6 months). Capsules, trailer, and screenshots matter more than your lovingly hand-tuned ECS architecture.
  • Playtest + demo: run a closed or open playtest to generate wishlists and fix device issues.
  • Next Fest: massive wishlist driver if your demo is strong. Time your feature readiness.
  • Pricing: align with completion time and replayability. Typical indie bands: $4.99–$19.99. A 10% launch discount is standard.

Example project layout (Unity)

  • Assets/Game — gameplay, scenes, prefabs
  • Assets/Plugins/Steamworks — Steamworks.NET
  • Assets/Resources/Config — JSON balance/config
  • Packages/ — pinned package versions
  • Build/ — CI artifacts (cleaned from repo)
  • Tools/BuildScripts — editor build scripts, Steam VDF templates

Editor build script concept:

using UnityEditor;
using UnityEngine;

public static class CIBuilder {
    [MenuItem("CI/Build Windows64")]
    public static void BuildWin64() {
        var scenePaths = new[] {
            "Assets/Game/Scenes/Boot.unity",
            "Assets/Game/Scenes/MainMenu.unity",
            "Assets/Game/Scenes/Level01.unity"
        };
        var options = new BuildPlayerOptions {
            scenes = scenePaths,
            locationPathName = "Build_Windows64/Game.exe",
            target = BuildTarget.StandaloneWindows64,
            options = BuildOptions.CompressWithLz4HC
        };
        PlayerSettings.SplashScreen.show = false;
        PlayerSettings.bundleVersion = BuildInfo.Version;
        BuildPipeline.BuildPlayer(options);
    }
}

Testing checklists that save launches

  • Cold start in <10 seconds on a mid-tier laptop (SSD). If not, add a loading screen with visible progress.
  • Offline mode: achievements queue, cloud saves defer, no crash on DNS failures.
  • Overlay: Shift+Tab opens cleanly in gameplay and menus. No input lock.
  • Controller parity: fully playable with controller only, including naming and menus.
  • Proton: run under Proton GE on Steam Deck and a desktop Linux. Fix filesystem case issues.
  • Multi-monitor: windowed/fullscreen transitions, alt-tab stability.
  • Antivirus: ensure no false-positive flags. Sign binaries if possible.

One final QA trick: ship a hidden perf overlay (FPS, frame time, build SHA) toggled by a debug key for playtest builds. You’ll thank yourself when tracking performance regressions.

What breaks in production

  • Cloud save conflicts: players switching PCs mid-session, or clock skew causing "newer" saves to be overwritten. Add timestamps and a clear conflict UI.
  • Corrupted saves after abrupt quits: persist via temp files and atomic renames; checksum your save files.
  • Shader compilation stalls: pre-warm commonly used shaders and ship a cache. Otherwise the first post-launch hotfix note will be "fixed stutters on first load".
  • Patch sizes ballooning: a small texture tweak invalidates a large asset bundle. Split asset bundles logically; keep depots granular.
  • Steam Overlay input deadlocks: custom input wrappers fighting the overlay. Use Steam Input as the source of truth.
  • Localized fonts exploding memory: dynamic font atlases balloon with CJK; pre-bake font atlases by language.
  • Multiplayer desync: naïve client authority in public lobbies invites cheating and desync. Move critical logic authoritative-side or switch to dedicated servers.

Cost, timeline, and ROI: the unglamorous math

These are realistic ranges we see as builders — not marketing theater.

  • Steam Direct fee: $100 per app.
  • Team makeup (lean): 1–2 gameplay engineers, 1 tech artist/generalist, 1 designer/producer, part-time audio. Contract art/QA as needed.
  • Timeline (small 2D/3D game): 6–12 months to a polished v1 if part-time, 4–8 months full-time.
  • Budget (development only): $60k–$300k, widely variant with scope, art style, and multiplayer complexity.
  • External costs: trailer ($2k–$10k), key art/capsules ($500–$3k), QA passes ($5k–$30k), localization ($1k–$10k per language set), backend hosting ($50–$2k/month early).
  • Revenue share: Valve takes ~30%. Taxes and VAT handling are built-in; complete the tax interview in Partner site to avoid excessive withholding.

ROI levers you control:

  • Scope cuts that reduce QA matrix (fewer platforms, fewer input modes) yield disproportionate time savings.
  • Strong demo and Next Fest presence often outperforms extra features added late.
  • Price to the experience; discount cadence matters more than a $1 price change.

When to choose what (trade-offs)

DecisionOption AOption BTrade-off we see most
Linux supportProton-onlyNative Linux buildProton is fast to ship; native buys fewer edge bugs, costs ongoing QA
MultiplayerSteam P2PDedicated serversP2P ships fast for co-op; servers cost more, reduce cheating, better for PvP
EngineUnityUnrealUnity: faster for small teams; Unreal: visual fidelity, heavier tooling
LeaderboardsSteamCustom backendSteam is instant but Steam-only; custom unlocks cross-platform, adds ops load
AnalyticsMinimalDeep event pipelineMinimal protects perf and privacy; deep analytics aid tuning, add compliance burden

Security and privacy basics

  • Don’t log SteamIDs alongside PII unless necessary; hash or pseudonymize for analytics.
  • Provide a clear privacy policy link in-game and on the store page if you collect telemetry.
  • Code-sign builds where you can; avoid shipping unsigned DLLs that trip SmartScreen.
  • Keep native plugins updated; stale DLLs are a common crash vector.

A note on content updates and DLC

  • Plan a post-launch content cadence. Even a small quality-of-life patch plus a free cosmetic goes a long way.
  • If you sell DLC, make it a separate depot. Verify base-game without DLC doesn’t reference DLC assets to avoid missing file crashes.
  • Avoid paywalled bugfixes; tie paid updates to clear content value.

Checklists for the Steam Partner backend

  • App metadata: descriptions, tags, content warnings, supported languages.
  • Pricing and regional pricing review.
  • Achievements uploaded and tested (developer tools tab shows unlocks).
  • Cloud save patterns configured; upload test with multiple PCs.
  • Build review: set public release date/time, initial discount, feature capsules localized.
  • Playtest: close it before launch or convert to a demo.

Common refactors web teams underestimate

  • Asset management: splitting bundles like you’d split JS chunks. Except mismatched versions hard-crash, not just 404.
  • Determinism for networking: floating-point accumulation and nondeterministic physics differ across FPS. Fix with fixed timesteps and server reconciliation.
  • Performance budgets: a single unbatched UI canvas can cost more than your React app’s whole render; profile in build, not only in editor.

A little dry wit: your 30ms GC spike doesn’t care that Lighthouse gives you 100/100.

Launch day ops

  • Freeze content 72 hours before, ship to staging, smoke test, then promote to public.
  • Staff Discord/Steam forums; pin known issues and hotfix ETA.
  • Hotfix flow: branch from release tag, cherry-pick, build, upload to beta, verify, promote.
  • Post-mortem: crash rate, top offenders, time-to-first-play, achievement funnel.

If you must self-host services

  • Matchmaking: a simple Go or Node service with Redis for lobbies is enough. Expose via Cloudflare; rate-limit by IP and SteamID.
  • Leaderboards: Postgres table with composite index (user_id, score DESC, created_at). Cache global top-N in Redis, paginate with cursors.
  • Telemetry: batch with a file-based queue, flush on quit or interval. Respect offline mode.
// Example: simple signed telemetry event structure ( TypeScript )
type Event = {
  t: number; // epoch ms
  v: string; // build version
  uid: string; // hashed SteamID
  name: string; // event name
  props?: Record<string, string | number | boolean>;
  sig: string; // HMAC signature
};

Keep events sparse. If you need deep analytics, consider sampling or opt-in.

Final pre-flight

  • Verify store page release visibility (public at launch time).
  • Validate depots map to the right branches.
  • Check achievements are visible publicly (not developer-only).
  • Run through a clean Windows user without dev tools installed.
  • Test Steam Deck controls and resolution (1280x800) if you want the Verified badge.

FAQ

Do I need a native Linux build or is Proton enough?

For many Unity/Unreal titles without kernel-level anti-cheat, Proton is sufficient and reduces QA load. If you want Steam Deck Verified or have Linux-specific optimizations, a native build helps but costs ongoing support.

Can I ship without Steamworks features?

Yes, but at minimum integrate achievements, Steam Input, and (if applicable) cloud saves. These are expected by players and reduce support tickets.

How do I handle Steam Guard in CI?

Create a dedicated build account with limited permissions. Enroll it in mobile auth, generate a login token, and store secrets in your CI. Rotate periodically. Avoid sharing your main developer account.

What about price and discounts?

Price to the experience length and niche. A 10% launch discount is common. Plan seasonal discounts and avoid heavy discounting too early; protect perceived value.

Should I build my own matchmaking or use Steam?

Use Steam Networking + lobbies if you’re Steam-only and co-op. If you plan cross-platform or competitive PvP, abstract it now and budget for dedicated servers.

How big should my first patch be?

Keep hotfixes under a few hundred MB by isolating volatile assets into their own asset bundles and depots. Frequent multi-GB patches train users to uninstall.

Key takeaways

  • Treat your Steam build like a web release with stricter QA: automate builds, version visibly, and gate promotion by smoke tests.
  • Integrate core Steamworks features early (achievements, input, cloud saves) to reduce last-minute platform bugs.
  • For small co-op, Steam Networking Sockets is the shortest viable path; go authoritative servers for PvP or security.
  • Proton often covers Linux; ship native only if you have the capacity and ROI.
  • Keep depots granular and asset bundles split to control patch sizes and rollbacks.
  • Budget for store assets, a demo, and Next Fest timing — these move revenue more than another feature week.

If you’re building your first Steam title and want a production-grade pipeline, MTBYTE can help with engine selection, CI/CD, Steamworks integration, and launch ops. Reach out at /contact and let’s de-risk your ship date.

NEXT STEP

Liked the approach?

We apply the same principles to client projects: AI, automation, products that don't die after launch.