Steamworks for JavaScript and HTML5 Games
Short answer: GemShell gives a JavaScript game the Steamworks API as one global steam object — 137 calls in 23 areas, from achievements and cloud saves to lobbies, P2P networking, the Workshop, Steam Input, the inventory and the recording timeline. Every call is async JavaScript. There is no SDK to link, no native module to compile per platform, and nothing to install in your game.
javascript
if (typeof steam !== 'undefined' && await steam.isAvailable()) {
const name = await steam.getPersonaName()
await steam.unlockAchievement('FIRST_BLOOD')
}It works in any engine that can run JavaScript — Construct 3, GDevelop, Phaser, Three.js, PixiJS, Kaplay, RPG Maker MV/MZ plugins, or a game you wrote yourself.
Setting it up
- Open your game in GemShell and turn on Steamworks.
- Enter your App ID — or
480(Valve's test app) while you develop. - Build. The
steamobject is there when the game starts.
Without Steam running, every call answers a harmless default instead of throwing, so the same build runs on Steam, on itch.io and on a machine with no Steam at all.
What you can do
Achievements and stats
javascript
await steam.unlockAchievement('BOSS_DOWN')
await steam.setStatInt('enemies_defeated', total)
await steam.storeStats()Cloud saves
javascript
await steam.fileWrite('slot1.json', JSON.stringify(save))
const save = JSON.parse(await steam.fileRead('slot1.json'))Leaderboards
javascript
await steam.uploadLeaderboardScore('Fastest Run', timeMs)
const top = await steam.downloadLeaderboardEntries('Fastest Run', 0, 0, 10)Multiplayer lobbies and P2P
javascript
const lobbyId = await steam.createLobby(1, 4) // friends-only, 4 players
await steam.sendP2PPacket(friendId, bytes, 2, 0)Invites and join requests
A friend clicking "Join Game" arrives as an event:
javascript
let e
while ((e = await steam.popEvent())) {
if (e.type === 'lobbyJoinRequested') await steam.joinLobby(e.lobbyId)
}And the rest
Rich presence, the Steam overlay, the Workshop (subscribed items are downloaded and mounted for you), Steam Input for controllers, the Steam Inventory, Steam Game Recording's timeline, Steam Music, voice chat, Family View, DLC checks, auth tickets for your own servers, and the Steam Deck keyboard.
Every call, by area: Steamworks coverage.
TypeScript
GemShell ships complete TypeScript definitions, so your editor autocompletes every Steam call and its arguments.
Questions
Do I need to compile a native Steam module?
No. GemShell loads Steam's own library itself on each platform. Your game only ever sees JavaScript.
Does it work on the Steam Deck?
Yes — the Linux build runs on the Steam Deck, and steam.isSteamDeck() tells the game it is on one, for instance to show controller prompts.
Can I test without my own App ID?
Yes. Use App ID 480 while developing; switch to your own before release.