A lightweight offline-first Tambola (Housie) Host Web App for game conductors. This is a host console to manage the game flow, numbers, winners, and budget.
- 🎲 Number Generation: Randomized 1-90 numbers with no duplicates
- 🔊 Voice Announcements: Browser-native TTS with traditional Tambola phrases
- 💰 Budget Management: Track prizes, payouts, and remaining budget
- 🏆 Winner Declaration: Easy prize assignment to players
- 📴 Fully Offline: Works without internet - data stored in IndexedDB
- 📱 Mobile-Friendly: Responsive design with large touch targets
- 💾 Persistent State: Game state survives refresh, tab close, browser restart
- React 18 + Vite - Fast, modern frontend
- TypeScript - Type safety
- Zustand - Lightweight state management
- Dexie.js - IndexedDB wrapper for persistence
- CSS Modules - Scoped styling
- Node.js 18+
- npm or yarn
# Install dependencies
npm install
# Start development server
npm run devThe app will be available at http://localhost:5173
npm run buildOutput will be in the dist folder.
src/
├── components/ # Reusable UI components
│ ├── Button.tsx
│ ├── NumberBoard.tsx
│ ├── CurrentNumber.tsx
│ ├── CalledHistory.tsx
│ ├── Modal.tsx
│ ├── WinnerModal.tsx
│ ├── Header.tsx
│ └── Toggle.tsx
├── pages/ # Route pages
│ ├── Home.tsx
│ ├── GameSetup.tsx
│ ├── GameScreen.tsx
│ ├── Summary.tsx
│ └── PastGames.tsx
├── store/ # Zustand store
│ └── gameStore.ts
├── db/ # Dexie database config
│ └── index.ts
├── lib/ # Utilities
│ ├── gameEngine.ts
│ └── tts.ts
├── data/ # Static data
│ └── numberPhrases.ts
├── types/ # TypeScript types
│ └── index.ts
└── styles/ # Global styles
└── index.css
- Click "New Game" on the home screen
- Set your total budget
- Configure prize categories (name + amount)
- Add player names
- Toggle voice on/off
- Click "Start Game"
- Press the large PLAY button to call the next number
- Numbers are announced via voice (if enabled)
- The 1-90 board highlights called numbers
- Called history shows recent numbers
- Click "Declare Winner"
- Select the prize category
- Select the winning player
- Confirm
- Click "End Game" to finish
- View the summary with:
- Per-player winnings
- Prize-wise payouts
- Total payout
- Remaining budget
Game {
id: string
date: number
budget: number
prizeStructure: Prize[]
players: Player[]
shuffledNumbers: number[]
currentIndex: number
calledNumbers: number[]
wins: Win[]
voiceEnabled: boolean
status: 'setup' | 'active' | 'completed'
}
Prize {
id: string
name: string
amount: number
}
Player {
id: string
name: string
}
Win {
id: string
gameId: string
playerId: string
prizeId: string
amount: number
timestamp: number
}MIT