A modern web application to automate your family Secret Santa gift exchange with real-time synchronization across devices.
- ✅ Interactive Mystery Selection - Players choose from mystery gift boxes without knowing who's inside
- ✅ Pre-configured family structure - Define family members and clics (family units)
- ✅ Real-time updates - All participants see draws happen live (with Pusher)
- ✅ Automatic validation - No self-draws, no drawing within your own clic with backtracking algorithm
- ✅ Sequential draws - Pre-defined order with narrative prompts and visual feedback
- ✅ Dramatic reveals - Multi-stage suspense animations with confetti celebration
- ✅ Live pool display - See who's still available and who's been drawn in real-time
- ✅ Mobile-friendly - Beautiful responsive UI optimized for all devices
- ✅ Local-only mode - Works without Pusher configuration for single-device use
- ✅ Role-based access control - Admin and player roles with session management
- ✅ Turn locking - Only the current drawer can make selections (server-side validation)
- ✅ Connection tracking - See which players are online/offline in real-time
- ✅ Admin controls - Skip turns, draw for absent players, and full game management
npm installEdit lib/family-config.ts to set up your family structure:
export const familyConfig: FamilyConfig = {
clics: [
{ id: "clic1", name: "Family Unit 1", memberIds: ["alice", "bob"] },
{ id: "clic2", name: "Family Unit 2", memberIds: ["charlie", "diana"] },
// Add more clics as needed
],
members: [
{ id: "alice", name: "Alice", clicId: "clic1" },
{ id: "bob", name: "Bob", clicId: "clic1" },
// Add all family members
],
drawOrder: ["alice", "bob", "charlie", "diana"],
};If you want real-time synchronization across multiple devices:
- Create a free account at Pusher
- Create a new Channels app
- Copy
.env.exampleto.env.local - Fill in your Pusher credentials
cp .env.example .env.localNote: If you skip this step, the app will work in local-only mode (perfect for a single device).
npm run devOpen http://localhost:3000 in your browser.
For Admin:
- Access the game with the admin URL:
http://localhost:3000?admin=<your-secret-code> - Replace
<your-secret-code>with yourADMIN_SECRET_CODEfrom.env.local - You'll have full control and see the admin panel
For Players:
- Share the regular URL:
http://localhost:3000 - Players select their name on the landing page
- They can only interact during their turn
- All players see real-time updates
If using Pusher, all family members can join on their own devices and everything syncs in real-time!
- Player Login - Each family member selects their name on the landing page
- Admin Access - Admin joins via URL with secret code for full control
- Turn Announcement - Each person's turn is announced with festive narrative prompts
- Turn Control - Only the current drawer (or admin) can start their turn
- Mystery Selection - The current player sees 2-5 mystery gift boxes (depending on valid options)
- Choice - Player clicks one mystery box to make their selection (server validates it's their turn)
- Suspense Build-up - Dramatic animation sequence:
- Opening mystery gift...
- Box opening animation
- Big reveal with confetti!
- Validation - The app automatically ensures using backtracking algorithm:
- No one draws themselves
- No one draws someone in their own clic
- Every choice leads to a completable game
- Server-side validation prevents unauthorized actions
- Pool Tracking - Sidebar shows remaining available people and who's been drawn
- Real-time Sync - When someone makes a selection, everyone sees it happen simultaneously
- Connection Status - Admin sees which players are online/offline
- Admin Controls - Admin can skip turns or draw for absent players if needed
- Next Turn - Automatic progression to the next person in the draw order
- Push your code to GitHub
- Import the repository in Vercel
- Add your Pusher environment variables in Vercel project settings
- Deploy!
Vercel will automatically build and deploy your app. Share the generated URL with your family.
In Vercel dashboard, add:
NEXT_PUBLIC_PUSHER_KEY- Your Pusher app keyNEXT_PUBLIC_PUSHER_CLUSTER- Your Pusher cluster (e.g., us2)PUSHER_APP_ID- Your Pusher app IDPUSHER_SECRET- Your Pusher secret keyADMIN_SECRET_CODE- Your secret code for admin access (e.g., family-secret-2025)
secret-santa/
├── app/
│ ├── layout.tsx # Root layout
│ ├── page.tsx # Main game screen with session check
│ ├── identify/page.tsx # Player identity selection page
│ └── api/
│ ├── session/
│ │ ├── identify/route.ts # Player login
│ │ ├── admin/route.ts # Admin login
│ │ ├── status/route.ts # Check session
│ │ ├── logout/route.ts # Logout
│ │ └── heartbeat/route.ts # Connection tracking
│ ├── draw/
│ │ ├── options/route.ts # Prepare selection options (authenticated)
│ │ └── select/route.ts # Finalize player selection (authenticated)
│ ├── admin/
│ │ ├── skip-turn/route.ts # Skip player's turn
│ │ └── draw-for-player/route.ts # Draw for absent player
│ └── state/route.ts # Game state sync endpoint
├── components/
│ ├── GameBoard.tsx # Main game orchestrator with role logic
│ ├── MysterySelector.tsx # Interactive gift box selection
│ ├── NarrativePrompt.tsx # Story-driven turn announcements
│ ├── WaitingForTurn.tsx # Waiting state for non-current players
│ ├── YourTurnNotification.tsx # Turn notification for current drawer
│ ├── AdminPanel.tsx # Admin control panel (floating)
│ ├── AdminDrawForPlayer.tsx # Admin draw dialog
│ ├── PlayerConnectionStatus.tsx # Online/offline player status
│ ├── PoolDisplay.tsx # Live available/drawn players sidebar
│ ├── RevealAnimation.tsx # Multi-stage reveal with confetti
│ ├── PlayerCard.tsx # Individual player status
│ └── ResultsDisplay.tsx # Final results display
├── lib/
│ ├── game-logic.ts # Selection algorithm & backtracking
│ ├── session.ts # Session validation & cookie handling
│ ├── pusher.ts # Pusher client/server setup
│ ├── family-config.ts # Family structure data
│ └── store.ts # State management (Zustand)
├── hooks/
│ ├── usePusher.ts # Real-time sync hook
│ └── useHeartbeat.ts # Connection heartbeat hook
└── types/
└── index.ts # TypeScript interfaces
- Next.js 15 - React framework with App Router
- TypeScript - Type safety
- Tailwind CSS - Styling
- Framer Motion - Advanced animations and transitions
- Zustand - State management
- Pusher - Real-time WebSocket communication
- Vercel - Hosting platform
Edit app/globals.css to customize colors and animations.
Edit lib/game-logic.ts to change validation rules or draw algorithm.
See PLAN.md for future enhancement ideas!
Q: The app won't start
- Make sure you ran
npm install - Check that Node.js version is 18+ (
node --version)
Q: Draws aren't syncing across devices
- Verify Pusher credentials in
.env.local - Check browser console for errors
- Ensure all devices are on the same app URL
Q: Getting "No valid options" error
- This should rarely happen due to the backtracking algorithm
- If it does occur, click "Reset Game" to restart
- The algorithm ensures only choices that lead to a complete game are presented
Q: How many options will each person see?
- The number varies (typically 2-5 mystery boxes)
- Only valid choices that guarantee a completable game are shown
- Later in the draw order, fewer options may be available
Q: Can players see who's in each box?
- No! That's the mystery element
- Players only see numbered gift boxes
- They can see the list of remaining people in the sidebar, but not which box contains whom
Q: How do I access the admin panel?
- Add
?admin=<your-secret-code>to the URL - Example:
http://localhost:3000?admin=family-secret-2025 - Use the secret code from your
ADMIN_SECRET_CODEenvironment variable - Admin sees a floating purple panel in the bottom-right
Q: What happens if a player disconnects?
- Their status shows as offline in the admin panel
- Admin can skip their turn or draw for them using admin controls
- When they reconnect, they'll see the updated game state
Q: Can someone cheat and select when it's not their turn?
- No! Server-side validation prevents unauthorized actions
- Only the current drawer (or admin) can make selections
- Attempts to act out of turn are rejected with a 403 error
Q: How does the admin "Draw for Player" feature work?
- Click the "Draw for Player" button in the admin panel during selection phase
- Select one of the mystery boxes on behalf of the absent player
- The selection is made as if the player did it themselves
- Everyone sees the reveal animation normally
MIT License - feel free to use and modify for your family! Web-based application for a family secret Santa draw, with special rules for our family annual draw.