double-elimination
v1.2.2
Published
Generate double elimination tournament brackets with automatic seeding and bye handling
Maintainers
Readme
double-elimination
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?
- When to Use This Package
- Features
- Installation
- Quick Start
- Use Cases
- API
- Bracket Structure
- Match Routing
- Bye Handling
- Delayed Losers Bracket and Single Elimination
- Seeding
- Example Output
- Performance
- Contributing
- License
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-eliminationQuick 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-advancesByes 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=4thPerformance
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
