Skip to content

Steam Timeline ​

Steam Game Recording keeps a timeline beside the recording, and a game can write on it: what the player is doing now, and moments worth finding again. Players see it while scrubbing a recording, and Steam uses it to suggest clips.

None of it is required and none of it costs anything when the player is not recording.

Methods ​

setTimelineTooltip(description, delta?) ​

Says what the player is doing right now. delta shifts it in seconds against now — negative for a moment already past.

javascript
await steam.setTimelineTooltip('Fighting the Warden');

setTimelineStateDescription() is the same call under the name Steam gave it first. Either will do.

clearTimelineTooltip(delta?) ​

Takes the description back off.

clearTimelineStateDescription() is the same call.

addTimelineEvent(icon, title, description?, priority?, startOffset?, duration?, clipPriority?) ​

Marks a moment.

  • icon — one of Steam's built-in icon names (steam_attack, steam_death, steam_achievement, steam_challenge, steam_bookmark and the rest), or one your app ships.
  • title — the short label on the timeline.
  • priority — 0–255; what survives when the timeline gets crowded.
  • startOffset — seconds back from now, for something that already happened.
  • duration — give this to mark a stretch of time rather than a moment.
  • clipPriority — 0 none, 1 standard, 2 featured: how much Steam should consider clipping it on its own.
javascript
await steam.addTimelineEvent('steam_attack', 'Warden down', 'No deaths', 80, 0, 0, 2);

Examples ​

Following the game's state ​

javascript
async function enterArea(area) {
  await steam.setTimelineTooltip(`Exploring ${area.name}`);
}

async function enterBossFight(boss) {
  await steam.setTimelineTooltip(`Fighting ${boss.name}`);
}

async function returnToMenu() {
  await steam.clearTimelineTooltip();
}

Marking what a player would want to find again ​

javascript
async function onBossDefeated(boss, secondsFought, deaths) {
  await steam.addTimelineEvent(
    'steam_attack',
    `${boss.name} defeated`,
    deaths === 0 ? 'No deaths' : `${deaths} deaths`,
    deaths === 0 ? 100 : 60,   // a clean kill matters more
    secondsFought,             // the fight started this long ago
    secondsFought,             // and lasted that long
    deaths === 0 ? 2 : 1       // worth a clip if it was clean
  );
}

async function onPlayerDeath(cause) {
  await steam.addTimelineEvent('steam_death', 'Died', cause, 30);
}

A personal best ​

javascript
async function onLapFinished(time, isBest) {
  if (!isBest) return;
  await steam.addTimelineEvent(
    'steam_challenge',
    'Personal best',
    formatTime(time),
    120, 0, 0, 2
  );
}

Notes ​

  • Keep descriptions short. They are read at a glance while scrubbing.
  • Spend high priority and clipPriority sparingly, or everything is important and nothing is.
  • Without Steam running these answer false and do nothing.