Skip to content

The @randsum/games/blades subpath provides mechanics for Blades in the Dark, a tabletop RPG about a crew of daring scoundrels seeking their fortunes on the haunted streets of an industrial-fantasy city.

Terminal window
bun add @randsum/games
import { roll } from '@randsum/games/blades'
// Roll with a dice pool of 3
const { result } = roll(3)
console.log(result) // 'critical' | 'success' | 'partial' | 'failure'
import { roll } from '@randsum/games/blades'
roll(0) // Zero dice: rolls 2d6, keeps lowest
roll(1) // One die
roll(2) // Two dice
roll(3) // Three dice
roll(4) // Four dice (e.g., with assistance)
roll(5) // Five dice (e.g., with assistance + push)
roll(6) // Six dice (maximum pool)
import { roll } from '@randsum/games/blades'
const { result } = roll(2)
switch (result) {
case 'critical':
// Multiple 6s - exceptional outcome
break
case 'success':
// Highest die is 6 - full success
break
case 'partial':
// Highest die is 4-5 - success with complication
break
case 'failure':
// Highest die is 1-3 - things go badly
break
}

Input:

ParameterTypeDescription
ratingnumber (optional)Action rating / dice pool size (0-6)

Returns: GameRollResult with:

PropertyTypeDescription
resultBladesRollResult'critical' | 'success' | 'partial' | 'failure'
totalnumberSum of kept dice after modifiers
rollsRollRecord[]Raw dice data from the core roller
OutcomeConditionDescription
criticalMultiple 6sCritical success — exceptional outcome
successHighest die is 6Full success — you do it
partialHighest die is 4-5Partial success — you do it, but with a complication
failureHighest die is 1-3Bad outcome — things go wrong

When the dice pool is 0, the system rolls 2d6 and keeps only the lowest die. This makes failure much more likely and prevents critical results.

This package handles the dice roll outcome only. Position (controlled, risky, desperate) and Effect (limited, standard, great) are narrative mechanics in Blades in the Dark that a caller manages separately — they are not part of the roll output. Use result to determine the mechanical outcome, then apply position and effect in your own game logic.

Game roll() can throw two types of errors:

  • ValidationError (from @randsum/roller) — numeric input out of range or not finite (e.g., rating of 7 when max is 6)
  • SchemaError (from @randsum/games/blades) — game-specific issues like unmatched outcome tables
import { roll, SchemaError } from '@randsum/games/blades'
import { ValidationError } from '@randsum/roller'
try {
roll(99)
} catch (error) {
if (error instanceof ValidationError) {
// Out-of-range rating (must be 0–6)
console.log(error.code) // 'VALIDATION_ERROR'
} else if (error instanceof SchemaError) {
// Game-specific error
console.log(error.code) // 'NO_TABLE_MATCH'
}
}
import type { BladesRollResult, GameRollResult, RollRecord } from '@randsum/games/blades'
import { SchemaError } from '@randsum/games/blades'

This game is powered by a .randsum.json spec that defines the dice pool, outcome tiers, and validation rules. The TypeScript code is generated from this spec at build time. See Schema Overview for how game specs work.