npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

double-elimination

v1.2.2

Published

Generate double elimination tournament brackets with automatic seeding and bye handling

Readme

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