steamworks.js: a Steamworks SDK binding for Electron and nw.js games
A steamworks implementation for nw.js/electron games
At a glance
- What is it?
- steamworks.js puts the Steamworks SDK behind a JavaScript API, implemented in Rust and shipped through napi with prebuilt binaries for four platforms, so a game can init Steam, read the local player and activate achievements without writing C++. The catch is that the renderer side of Electron has to be opened up to make it work.
- Who is it for?
- steamworks.js fits a studio shipping a Steam game from an Electron or nw.js shell that wants the Steamworks API in JavaScript with TypeScript declarations and no per-machine build step. It is the wrong choice if you cannot relax your Electron renderer security settings, since the documented configuration turns off context isolation and enables node integration, and it is the wrong choice for a native game, which should use the official SDK directly.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Activity is slowing. The repository last received commits 6 months ago.
- What is it written in?
- Mainly Rust, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The Steamworks SDK reached from JavaScript
The README describes steamworks.js as a modern implementation of the Steamworks SDK for HTML/JS and NodeJS based applications, and the repository description narrows the target to nw.js and Electron games. The point of the project is to put Valve's SDK behind a JavaScript API.
The implementation is in Rust, and the binding layer is napi, the Node-API. The Cargo manifest names the crate `steamworksjs`, sets its crate type to `cdylib`, and pulls in the `napi` and `napi-derive` crates with the `tokio_rt`, `napi6` and `serde-json` features, plus `tokio` itself with the `sync` and `time` features. Underneath it sits the `steamworks` crate, pinned to a specific commit of a Git repository rather than a version range, with the `serde` feature enabled.
That pinning is worth noticing. Depending on an upstream crate by git revision with a fixed rev means the build is reproducible, but it also means the binding is pinned to a moment in that upstream's history, and any update is a deliberate act rather than a routine dependency bump.
The release profile turns on `lto = true`, which is a sensible choice for a native module whose size is added to every shipped game, and the whole point of the design is that a JavaScript developer never touches that layer.
The list of reasons it was written
The README includes a Why section, and it is a list of grievances with an existing project, which makes it the most informative part of the documentation.
The author says they used greenworks for a long time and that it is great, but gave up for a set of reasons: it is not being maintained anymore, it is not up to date, it is not context-aware, you have to build the binaries by yourself, it does not have TypeScript definitions, the API is not trustful, and the API implements callbacks instead of returning flags or promises. The last line of the list is simply that the author hates C++.
Read as engineering requirements, those items define what steamworks.js is trying to be. A maintained and current implementation, a context-aware API so a call can be tied to the request that caused it, TypeScript definitions so the API is discoverable in an editor, prebuilt binaries so no local build is needed, and a promise or return-value style instead of callbacks.
The trust point is the most interesting to argue about. A callback-based API is not wrong, but it pushes error handling to the edges of the call, whereas a promise or a returned flag lets the caller branch on the result. The example in the README reflects that choice exactly, since the achievement call is used directly in an if statement.
init, localplayer and achievement, and where the API is documented
The API example in the README is short enough to read in one glance:
const steamworks = require('steamworks.js')
// You can pass an appId, or don't pass anything and use a steam_appid.txt file
const client = steamworks.init(480)
// Print Steam username
console.log(client.localplayer.getName())
// Tries to activate an achievement
if (client.achievement.activate('ACHIEVEMENT')) {
// ...
}Three things to take from it. The library is required as a plain CommonJS module. Initialisation takes an app id, 480 in the example, or you can pass nothing and use a `steam_appid.txt` file instead, which is the convention a game already following Valve's own documentation would expect. And the client object is namespaced, with `localplayer` and `achievement` as the two groups shown.
The activation call returns a value you branch on, which is the return-flag style the Why section asks for.
For the full surface, the README points at the declarations file in the repository, client.d.ts, to check the API support and get more detailed documentation of each function. The package ships three declaration files, `index.d.ts`, `client.d.ts` and `callbacks.d.ts`, which is a hint that the callback support from the underlying SDK is exposed alongside the promise-style API rather than discarded.
Electron's renderer has to be opened up for it
This is the part that will decide whether you can use the library at all. The README states that steamworks.js is a native module and cannot be used by default in the renderer process, and that to enable the usage of native modules on the renderer process, the following configurations should be made on `main.js`:
const mainWindow = new BrowserWindow({
// ...
webPreferences: {
// ...
contextIsolation: false,
nodeIntegration: true
}
})Context isolation off and node integration on is the standard Electron configuration for a game that needs native modules, and it is also the configuration that removes the isolation between the page and Node. For an application loading only your own content that is a reasonable trade. For an application that renders remote or untrusted content, it is not, and the library is the wrong tool for that shape of app.
The overlay is handled separately, by calling `electronEnableSteamOverlay` on the end of your `main.js` file:
require('steamworks.js').electronEnableSteamOverlay()For the production build, the relevant distro files have to be copied from `sdk/redistributable_bin/{YOUR_DISTRO}` into the root of the build, and for electron-forge users the README points at issue 75 for the specifics. That is the part of the integration that a package manager can handle and that a build pipeline has to be taught.
Consuming is one npm command, building is a different job
The installation section makes the division of labour explicit. To use steamworks.js you do not have to build anything, just install it from npm:
$: npm i steamworks.jsThe build section is qualified with a note that you only need to build if you are going to change something on steamworks.js code, and that if you are looking to just consume the library or use it in your game, you should refer to the installation section. In other words, the Rust toolchain is a contributor requirement, not a consumer one.
If you do want to build, the prerequisites are the latest node.js, Rust and Clang, and the README adds that Steam itself must be installed and running. The commands are `npm install` and then `npm run build:debug` to build the library. The package manifest shows what the release build does, since the `build` script runs the types step and then `node build --release`, and the `types` script generates the declaration files with `tsc index.js --allowJs --declaration --emitDeclarationOnly`. There is also a `format` script that runs clippy and cargo fmt, and a `prune` script that removes the dist and target directories along with the generated client.d.ts.
The build matrix is handled by GitHub. The README says there is no way to build for all targets easily, that you can develop and test on your current target and open a pull request, and that when the code is merged to main, a GitHub action will build for all targets and publish a new version. The workflow is named publish.yml, and the build badge at the top of the README points at it.
Four platforms ship as prebuilt binaries
The napi configuration in the package manifest lists the additional target triples the package provides prebuilt binaries for: `x86_64-pc-windows-msvc`, `x86_64-unknown-linux-gnu`, `x86_64-apple-darwin` and `aarch64-apple-darwin`.
Four targets is a meaningful boundary, and it is worth being explicit about what is missing. There is no 32-bit Windows entry, and the Apple list covers Intel macOS and Apple Silicon macOS but not iOS, even though the project is a JavaScript library that could plausibly run on a phone. A game shipped for Windows on 32-bit hardware, or for a Linux distribution on a non-x86-64 architecture, is outside the prebuilt set and would have to build from source.
The package metadata is otherwise lean. The version is 0.4.0, the main entry is `index.js` with types from `index.d.ts`, the napi module name is `steamworksjs`, and the published files are `dist/*`, `index.js`, the declaration files and the README. The declared engine is node >= 14, and the only runtime dependency is `@types/node` with a wildcard version.
The practical effect is that installing from npm gives you a native binary for your platform, and the cross-platform build is the maintainer's problem, handled by the publish workflow.
MIT licensed, version 0.4.0, and a branch last touched in April
The licence is MIT, with the LICENSE file at the root of the repository, and the README carries the matching badge.
The version is worth attention. The package manifest reads 0.4.0, which is early for a library that has to keep pace with both Node's ABI expectations and the Steamworks SDK itself, and the README does not make stability promises. A consumer pinning a version is pinning a young interface, and the declaration files are the thing to read before relying on a particular method, since the README explicitly points at them for the current API support.
On maintenance, the repository is not archived, and the last push was on 2026-04-08. The project has no GitHub releases, so the npm version is the release marker, and the publish workflow in the GitHub actions directory is what produces it.
The project also runs a Discord channel, linked from the badges at the top of the README, and that is where questions about the Electron setup or the overlay tend to land, since the README's own text on those points is short and points at an issue for the electron-forge case rather than documenting it.
Editorial conclusion
steamworks.js fits a studio shipping a Steam game from an Electron or nw.js shell that wants the Steamworks API in JavaScript with TypeScript declarations and no per-machine build step. It is the wrong choice if you cannot relax your Electron renderer security settings, since the documented configuration turns off context isolation and enables node integration, and it is the wrong choice for a native game, which should use the official SDK directly. Verify first that your target platform is among the four with prebuilt binaries, that Steam is running on the machine, and that the version you install from npm is the one whose API you have read.
Frequently asked questions
Do I need to build steamworks.js to use it in a game?
No. The installation section says you do not have to build anything, just install it from npm with npm i steamworks.js. Building is only needed if you are changing the code, and it requires node.js, Rust, Clang and a running Steam installation.
How do I initialise Steam with steamworks.js?
Call steamworks.init with an app id, for example steamworks.init(480), or pass nothing and use a steam_appid.txt file. The client object exposes groups such as localplayer and achievement.
Why does steamworks.js need Electron configuration changes?
The README states that steamworks.js is a native module and cannot be used by default in the renderer process, so main.js has to be configured with contextIsolation false and nodeIntegration true, and electronEnableSteamOverlay has to be called at the end of main.js.
Which platforms have prebuilt steamworks.js binaries?
The package manifest lists the additional target triples x86_64-pc-windows-msvc, x86_64-unknown-linux-gnu, x86_64-apple-darwin and aarch64-apple-darwin, and when code is merged to main a GitHub action builds for all targets and publishes a new version.
Where is the steamworks.js API documented?
The README points at the declarations file in the repository, client.d.ts, to check the API support and read the detailed documentation of each function. The package also ships index.d.ts and callbacks.d.ts as type declarations.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/ceifa-steamworks-js)Was this useful?