Skip to content

TypeScript library for generating double elimination tournament brackets with automatic seeding and bye handling

Notifications You must be signed in to change notification settings

nadersafa1/double-elimination

Repository files navigation

double-elimination

npm version npm weekly downloads License: MIT TypeScript GitHub stars Live Demo

A TypeScript library for generating double elimination tournament brackets with automatic seeding and bye handling. Perfect for esports tournaments, sports competitions, and any competitive event management system.

Table of Contents

Why Double Elimination?

Double elimination tournaments are the gold standard for competitive events because they:

  • Ensure fairness: Players must lose twice to be eliminated, reducing the impact of bad luck or upsets
  • Provide accurate rankings: Top 4 placements are determined through structured competition
  • Increase engagement: More matches mean more content and viewer engagement
  • Reduce early elimination: Strong players who face tough early matchups get a second chance

This library implements the standard double elimination format used in major esports tournaments, fighting game competitions, and sports events worldwide.

When to Use This Package

Use double-elimination when you need to:

  • Build tournament management systems for esports, sports, or gaming platforms
  • Generate bracket structures programmatically with proper seeding
  • Handle variable participant counts with automatic bye management
  • Support multiple tournament formats including single elimination and delayed double elimination
  • Ensure fair matchups using standard tournament seeding algorithms
  • Integrate tournament brackets into existing applications or websites

Perfect for developers building:

  • Esports tournament platforms
  • Sports competition management systems
  • Gaming tournament organizers
  • Bracket visualization tools
  • Tournament scheduling applications

Features

  • Complete bracket generation - Winners and losers bracket structures
  • Standard tournament seeding - Ensures seeds 1 and 2 can only meet in finals, seeds 1-4 can only meet in semifinals, etc.
  • Automatic bye handling - Handles participant counts that aren't powers of 2
  • Flexible tournament formats - Supports double elimination, single elimination, and delayed losers bracket
  • Rematch prevention - Intelligent routing prevents early rematches between players
  • TypeScript support - Full type definitions included
  • Zero dependencies - Lightweight and fast
  • Configurable ID generation - Use any ID factory function

Installation

npm install double-elimination

Quick Start

import { generateDoubleElimination } from 'double-elimination'

const matches = generateDoubleElimination({
  eventId: 'tournament-1',
  participants: [
    { registrationId: 'player-1', seed: 1 },
    { registrationId: 'player-2', seed: 2 },
    { registrationId: 'player-3', seed: 3 },
    { registrationId: 'player-4', seed: 4 },
  ],
  idFactory: () => crypto.randomUUID(),
})

API

generateDoubleElimination(options)

Generates all matches for a double elimination bracket.

Options

Property Type Description
eventId string Identifier for the tournament event
participants Participant[] Array of participants with seeds
idFactory () => string Function that returns unique IDs for matches
losersStartRoundsBeforeFinal number? Rounds before finals where LB begins (min: 0). See below.

Returns

BracketMatch[] - Array of all matches in the bracket

Types

interface Participant {
  registrationId: string
  seed: number
}

interface BracketMatch {
  id: string
  eventId: string
  round: number
  matchNumber: number
  registration1Id: string | null
  registration2Id: string | null
  bracketPosition: number
  winnerTo: string | null
  winnerToSlot: number | null
  loserTo: string | null
  loserToSlot: number | null
  bracketType: 'winners' | 'losers'
}

Bracket Structure

Placements

This library uses a simplified double elimination format without grand finals:

Match Winner Loser
Winners Bracket Finals 1st Place 2nd Place
Losers Bracket Finals 3rd Place 4th Place

Match Counts

For N participants:

  • Bracket size = next power of 2 ≥ N
  • Winners rounds = log₂(bracket_size)
  • Losers rounds = (winners_rounds - 1) × 2 - 1
Participants Bracket Size Winners Matches Losers Matches Total
4 4 3 1 4
5-8 8 7 5 12
9-16 16 15 13 28

Match Routing

Winners Bracket

  • Winner routing: Position P → next round, position ⌊P/2⌋, slot (P % 2) + 1
  • Loser routing: Drops to losers bracket (except finals loser who gets 2nd place)

Losers Bracket

  • Receives losers from winners bracket rounds 1 and 2
  • Winners from losers bracket finals get 3rd place, loser gets 4th

Cross-Bracket Matchups

To prevent early rematches, losers from the winners bracket are routed using a specific pattern:

  • Round 1 losers: Grouped by position pairs (floor(position/2))
  • Round 2 losers: REVERSED positions - players from top half face bottom half LB winners, and vice versa
    • WB R2 pos 0 (top half) → LB R2 pos N-1 (faces bottom half LB R1 winner)
    • WB R2 pos N-1 (bottom half) → LB R2 pos 0 (faces top half LB R1 winner)
  • Round 3+ losers: Use SAME positions (not mirrored) to continue the crossover pattern

This ensures players from opposite sides of the winners bracket meet in the losers bracket, preventing rematches until later rounds.

Bye Handling

When participant count isn't a power of 2, byes are automatically created and processed:

// 7 participants in bracket of 8 = 1 bye
const matches = generateDoubleElimination({
  eventId: 'event-1',
  participants: createParticipants(7), // Seeds 1-7
  idFactory: () => crypto.randomUUID(),
})

// Seed 1 vs Seed 8 (missing) = Seed 1 auto-advances

Byes are pre-resolved at generation time - the advancing player is already placed in the next round.

Delayed Losers Bracket and Single Elimination

By default, all losers (except finals) drop to the losers bracket. Use losersStartRoundsBeforeFinal to start the losers bracket later - early round losers are permanently eliminated.

Single Elimination Modes

You can create pure single elimination or single elimination with a 3rd place match:

// Pure single elimination (no losers bracket)
const matches = generateDoubleElimination({
  eventId: 'event-1',
  participants: createParticipants(8),
  idFactory: () => crypto.randomUUID(),
  losersStartRoundsBeforeFinal: 0, // No losers bracket
})

// Single elimination with 3rd place match (only semifinal losers)
const matches = generateDoubleElimination({
  eventId: 'event-1',
  participants: createParticipants(8),
  idFactory: () => crypto.randomUUID(),
  losersStartRoundsBeforeFinal: 1, // Only semifinal losers go to LB
})

Delayed Double Elimination

For delayed double elimination, use values >= 2:

// 16 players with losers bracket starting at Quarter-Finals
const matches = generateDoubleElimination({
  eventId: 'event-1',
  participants: createParticipants(16),
  idFactory: () => crypto.randomUUID(),
  losersStartRoundsBeforeFinal: 2, // QF and SF losers go to LB
})

How It Works

For 16 participants (4 WB rounds) with losersStartRoundsBeforeFinal: 2:

WB Round Name Loser Fate
Round 1 Ro16 Eliminated (single elim)
Round 2 QF Drops to LB R1
Round 3 SF Drops to LB R2
Round 4 Finals 2nd Place

Constraints

  • Minimum value: 0 - Pure single elimination (no losers bracket)
  • Value: 1 - Single elimination with optional 3rd place match (requires at least 4 participants)
  • Value: 2+ - Delayed double elimination
  • Maximum value: winnersRounds - 1 - Cannot exceed available feeder rounds

Seeding

The library uses standard tournament seeding to ensure fair bracket placement:

  • Seeds 1 and 2 can only meet in the Finals
  • Seeds 1-4 can only meet in Semifinals or later
  • Seeds 1-8 can only meet in Quarterfinals or later

For 8 participants, Round 1 matchups are: 1v8, 4v5, 2v7, 3v6

For 32 participants:

  • Seed 1 is in matches 0-7 (top half)
  • Seed 2 is in matches 8-15 (bottom half)
  • Seeds 3-4 are in opposite quarters from seeds 1-2

Use Cases

Esports Tournament Platform

Generate brackets for competitive gaming tournaments with proper seeding and fair matchups.

const esportsBracket = generateDoubleElimination({
  eventId: 'valorant-championship-2024',
  participants: teams.map((team, index) => ({
    registrationId: team.id,
    seed: team.rank,
  })),
  idFactory: () => crypto.randomUUID(),
})

Sports Competition Management

Create tournament brackets for sports leagues, ensuring top seeds don't meet until later rounds.

Gaming Tournament Organizer

Run local or online gaming tournaments with automatic bracket generation and bye handling.

Bracket Visualization

Generate bracket data for visualization libraries like D3.js, React components, or custom renderers.

Example Output

For 8 participants:

WINNERS BRACKET:
Round 1: [1v8, 4v5, 2v7, 3v6] → losers drop to LB R1
Round 2: [R1 winners]         → losers drop to LB R2
Round 3: [Finals]             → winner=1st, loser=2nd

LOSERS BRACKET:
Round 1: [WB R1 losers pair up]
Round 2: [LB R1 winners + WB R2 losers]
Round 3: [Finals] → winner=3rd, loser=4th

Performance

The library is optimized for performance:

  • Fast generation: Brackets are generated in O(n) time where n is the number of participants
  • Memory efficient: Minimal memory footprint with no external dependencies
  • Scalable: Handles tournaments from 4 to 1000+ participants efficiently
  • Zero runtime dependencies: No external packages required at runtime

Benchmark results (typical):

  • 8 participants: < 1ms
  • 32 participants: < 2ms
  • 128 participants: < 5ms
  • 512 participants: < 15ms

Contributing

Contributions are welcome! Please read our Contributing Guide for details on our code of conduct and the process for submitting pull requests.

Ways to Contribute

  • 🐛 Report bugs
  • 💡 Suggest new features
  • 📝 Improve documentation
  • 🔧 Submit pull requests
  • ⭐ Star the repository

License

MIT

About

TypeScript library for generating double elimination tournament brackets with automatic seeding and bye handling

Topics

Resources

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published