Skip to content

Steam Achievements ​

Unlock and query achievements.

Loading Stats ​

IMPORTANT

Before reading or writing stats, you must wait for Steam to load the stats data from the server. Call requestStatsAndWait() once at game start.

javascript
// At game start, after steam.init()
const statsReady = await steam.requestStatsAndWait(3000); // Wait up to 3 seconds

if (statsReady) {
  console.log('Stats loaded, ready to use achievements and stats!');
} else {
  console.warn('Could not load stats from Steam');
}

Stats State ​

You can check the current stats loading state:

javascript
const state = await steam.getStatsState();
// 0 = not requested
// 1 = pending (waiting for Steam)
// 2 = ready (can read/write stats)
// 3 = failed

// Or use the helper:
const ready = await steam.areStatsReady(); // true/false

Methods ​

unlockAchievement(name) ​

Unlock an achievement.

javascript
await steam.unlockAchievement('ACH_WIN_FIRST_GAME');
await steam.storeStats(); // Required to save

getAchievement(name) ​

Check if achievement is unlocked.

javascript
const unlocked = await steam.getAchievement('ACH_WIN_FIRST_GAME');
// true or false

clearAchievement(name) ​

Clear an achievement (for testing).

javascript
await steam.clearAchievement('ACH_WIN_FIRST_GAME');
await steam.storeStats();

storeStats() ​

Save achievements and stats to Steam.

javascript
await steam.storeStats();

WARNING

Always call storeStats() after modifying achievements or stats.

Stats ​

Stat names must match your App Admin schema exactly

If setStatInt / setStatFloat return false even though getStatInt works, the cause is almost always your Steamworks App Admin configuration. Check all of the following:

  1. The stat exists in your App Admin: Steamworks Partner site → Application → Stats & Achievements → Stats.
  2. The name matches 1:1 and is case-sensitive. TOTAL_KILLS, total_kills, and Total_Kills are three different stats. Copy/paste the name from App Admin into your code.
  3. The type matches. A stat configured as INT will refuse setStatFloat(), and vice-versa. AVGRATE stats use setStatFloat().
  4. The stat is published. New stats sit as a draft until you click "Publish" in the Steamworks dashboard. Until then they don't exist for any client, including yours.
  5. The value is inside [MinValue, MaxValue] if you configured a range in App Admin. Out-of-range writes silently fail.

If something still doesn't work, open stats_log.txt from your Steam client: Valve prints the exact reason there:

  • macOS: ~/Library/Application Support/Steam/logs/stats_log.txt
  • Linux: ~/.local/share/Steam/logs/stats_log.txt (or ~/.steam/steam/logs/stats_log.txt)
  • Windows: <Steam install>\logs\stats_log.txt

setStatInt(name, value) ​

Set an integer stat.

javascript
await steam.setStatInt('TOTAL_KILLS', 100);
await steam.storeStats();

getStatInt(name) ​

Get an integer stat. Returns 0 if stats are not loaded yet.

javascript
const kills = await steam.getStatInt('TOTAL_KILLS');

TIP

Make sure to call requestStatsAndWait() at game start before reading stats.

setStatFloat(name, value) ​

Set a float stat.

javascript
await steam.setStatFloat('TOTAL_DISTANCE', 1234.56);
await steam.storeStats();

getStatFloat(name) ​

Get a float stat. Returns 0 if stats are not loaded yet.

javascript
const distance = await steam.getStatFloat('TOTAL_DISTANCE');

requestStats() ​

Request stats from Steam (async, returns immediately).

javascript
await steam.requestStats();

requestStatsAndWait(timeoutMs) ​

Request stats and wait until they're loaded. Returns true if successful.

javascript
const ready = await steam.requestStatsAndWait(3000); // 3 second timeout

areStatsReady() ​

Check if stats have been loaded.

javascript
if (await steam.areStatsReady()) {
  // Safe to read/write stats
}

Examples ​

Basic Achievement ​

javascript
async function onBossDefeated(bossName) {
  // Achievement for first boss
  if (bossName === 'Dragon') {
    await steam.unlockAchievement('ACH_DEFEAT_DRAGON');
    await steam.storeStats();
  }
}

Progress Achievement ​

javascript
// At game start
async function initGame() {
  // Wait for stats to load from Steam
  const ready = await steam.requestStatsAndWait(3000);
  if (!ready) {
    console.warn('Stats not available');
  }
}

async function onEnemyKilled() {
  // Stats are already loaded, safe to read
  const kills = await steam.getStatInt('TOTAL_KILLS') || 0;
  await steam.setStatInt('TOTAL_KILLS', kills + 1);
  
  // Achievement at 100 kills
  if (kills + 1 >= 100) {
    await steam.unlockAchievement('ACH_100_KILLS');
  }
  
  await steam.storeStats();
}

Track Distance ​

javascript
let sessionDistance = 0;

function onMove(delta) {
  sessionDistance += delta;
}

async function onGameEnd() {
  const total = await steam.getStatFloat('TOTAL_DISTANCE') || 0;
  await steam.setStatFloat('TOTAL_DISTANCE', total + sessionDistance);
  
  // Achievement for 10km
  if (total + sessionDistance >= 10000) {
    await steam.unlockAchievement('ACH_MARATHON');
  }
  
  await steam.storeStats();
  sessionDistance = 0;
}

Multiple Achievements ​

javascript
const ACHIEVEMENTS = {
  firstWin: 'ACH_FIRST_WIN',
  perfectLevel: 'ACH_PERFECT',
  speedrun: 'ACH_SPEEDRUN',
  collector: 'ACH_ALL_ITEMS'
};

async function checkAchievements(gameState) {
  if (gameState.wins >= 1) {
    await steam.unlockAchievement(ACHIEVEMENTS.firstWin);
  }
  
  if (gameState.perfectLevels >= 1) {
    await steam.unlockAchievement(ACHIEVEMENTS.perfectLevel);
  }
  
  if (gameState.fastestTime < 60) {
    await steam.unlockAchievement(ACHIEVEMENTS.speedrun);
  }
  
  if (gameState.items.length >= 50) {
    await steam.unlockAchievement(ACHIEVEMENTS.collector);
  }
  
  await steam.storeStats();
}

Steamworks Dashboard ​

Define achievements in your Steamworks dashboard:

  1. Go to Steamworks Partner
  2. Select your app
  3. Navigate to Stats & Achievements
  4. Add achievements with API names
  5. Upload icons (64x64 locked, 64x64 unlocked)