ChoppedEats - Project Summary
🎯 Inspiration
Ever found yourself in a group chat going back and forth for 30 minutes trying to decide where to eat? We've all been there. The problem is simple but universal: when groups of friends need to agree on a restaurant, everyone has different preferences, dietary restrictions, budgets, and cravings. Traditional solutions like sending links in group chats or voting in Slack polls are inefficient and don't use real restaurant data effectively.
ChoppedEats was born from this everyday frustration. We wanted to create something that combines the best of modern technology—AI for understanding natural language preferences, real-time collaboration, and actual restaurant data—to make the "where should we eat?" conversation quick, democratic, and fun.
🚀 What it does
ChoppedEats is a real-time collaborative platform that helps groups decide where to eat through AI-powered recommendations and democratic voting. Here's how it works:
The Flow:
- Create a Party - Generate a shareable code (e.g., "67AU41")
- Share Your Vibes - Each person types what they're craving in natural language (e.g., "I want Italian food with an aesthetic view" or "Craving sushi, budget is $40")
- Get AI Recommendations - Our system analyzes all vibes and fetches personalized restaurant recommendations with Google Places API and Groq
- Curate Candidates - Add restaurants you like to the voting pool
- Swipe & Vote - Everyone swipes through candidates, casting votes
- Celebrate the Winner - The highest-voted restaurant wins with confetti! 🎉
Key Features:
- Real-time collaboration - See everyone's vibes and votes update live
- No signup required - Join parties instantly with a code
- Toggle voting - Change your mind? Click the button again to undo
- Beautiful UI - Modern gradients, confetti animations, and intuitive swiping
- Google Places integration - Real restaurant photos, ratings, addresses, and more
- AI-powered recommendations - Understands natural language preferences
🔧 How we built it
Tech Stack:
- Frontend: Next.js 14 (App Router), React, TypeScript, Tailwind CSS
- Backend: Next.js API Routes, Firebase/Firestore for real-time data
- APIs: Google Places API (restaurant data), Groq AI (NLP)
- Database: Firebase Firestore listeners with onSnapshot
Architecture:
User Flow:
1. Landing Page (create/join party)
2. Party Page (share vibes, see recommendations)
3. Voting Page (swipe through restaurants)
4. Results Page (winner with confetti celebration)
Key Technical Decisions:
Firebase for State Management
- Real-time synchronization across all devices using Firestore listeners
- No authentication required - uses session-based user IDs
- Automatic conflict resolution and state persistence
Google Places API Integration
- Fetches real restaurant data (name, photos, ratings, addresses)
- Implements photo fetching through proxy endpoint
- Smart cuisine type extraction from place types
Optimistic UI Updates
- Toggle buttons update instantly before API confirmation
- Reverts on error to keep UI in sync with server
- Provides immediate feedback to users
Swipe Interaction
- Built custom swipe gesture handler for restaurant cards
- Smooth animations and transitions
- Clear visual feedback for left/right swipes
💪 Challenges we ran into
1. Real-Time State Synchronization
Problem: Keeping all party members' views in sync when multiple users are adding vibes, voting, or modifying restaurants simultaneously.
Solution: Implemented Firebase Firestore listeners with onSnapshot that automatically updates when any user makes changes. We also added optimistic UI updates so users see their changes immediately, then sync with the server.
2. Toggle Voting Without Conflicts
Problem: Users should be able to add and remove their votes, but multiple rapid clicks could cause race conditions or inconsistent vote counts.
Solution: Implemented client-side optimistic updates that immediately toggle the vote UI, then sync with Firebase. Added error handling to revert changes if the API call fails. Used Set data structures to track added restaurants efficiently.
3. Photo Loading from Google Places
Problem: Google Places photos require authenticated requests and special photo name handling, which couldn't be done directly from the client.
Solution: Created a proxy API endpoint (/api/photo) that fetches and serves images server-side, handling authentication and photo reference resolution.
4. Confetti Animation on Winners Page
Problem: Wanted to celebrate the winner but confetti had to trigger only once, not re-trigger on every re-render.
Solution: Used useEffect with proper dependencies that only runs when the results phase starts and a winner exists. Made the animation duration-based rather than infinite to avoid performance issues.
5. Managing Background Pattern Opacity
Problem: Applied opacity to the background pattern, but it made all child elements semi-transparent too.
Solution: Created a separate absolute-positioned layer for the background with opacity, then positioned content above it with relative positioning and higher z-index.
🏆 Accomplishments that we're proud of
✅ Zero Authentication Required - Users can join parties instantly with a code, no signup flow needed
✅ Real-time Collaboration - All party members see vibes, votes, and updates instantly as they happen
✅ Beautiful UX - Confetti celebrations, smooth swipes, modern gradients, and intuitive interactions
✅ Robust State Management - Complex real-time state with multiple users, vibes, restaurants, and votes all syncing flawlessly
✅ Type-Safe Development - Full TypeScript implementation with interfaces for all data structures
✅ Production-Ready - Clean code, proper error handling, loading states, and responsive design
✅ No Polling - Efficient Firebase listeners instead of constant API calls
✅ Toggle Functionality - Users can undo their votes or restaurant selections easily
📚 What we learned
Firebase is powerful but requires careful listener management - We learned to always clean up subscriptions in useEffect return functions to prevent memory leaks
Optimistic UI is crucial for perceived performance - Immediate feedback makes the app feel responsive even when API calls take time
TypeScript interfaces are essential for large apps - Defining clear data structures upfront prevented countless bugs
Server-Sent Events vs WebSockets - Started with SSE, migrated to Firebase for bidirectional updates without managing connection lifecycle
Event-driven architecture scales well - Separating concerns (vibes, restaurants, voting) into different Firestore subcollections made the app more maintainable
Real-time apps need conflict resolution - Multiple users can perform actions simultaneously, so optimistic updates with revert-on-error is critical
Loading states matter - Fetching restaurant photos took time, so adding loading skeletons improved perceived performance significantly
🚀 What's next for ChoppedEats
Short Term:
- [ ] Dietary Filters - Vegetarian, vegan, gluten-free, kosher, halal
- [ ] Distance Filtering - "Only show restaurants within 2 miles"
- [ ] Map View - Visual restaurant locations on a map
- [ ] Multiple Voting Rounds - If a tie occurs, run another vote
- [ ] Restaurant Details Modal - Expand to see full reviews and more photos
Medium Term:
- [ ] User Accounts/Auth - Save favorite restaurants, preferences, and voting history
- [ ] Reservation Integration - Book tables directly from the app
- [ ] Group Scheduling - Find times that work for everyone
- [ ] Split Bill Calculator - Help divide the check fairly
- [ ] Mobile App - Native iOS/Android apps with push notifications
Long Term:
- [ ] Historical Tracking - "Where did we eat last month?" functionality
- [ ] Social Features - Friend groups, favorite restaurants list
- [ ] Restaurant Reviews Integration - Show Yelp/Google reviews in-app
- [ ] Theme Support - Custom party themes and celebrations
- [ ] Analytics Dashboard - Track group preferences over time
- [ ] Integration with Food Delivery - Add pickup/delivery options
Built with ❤️ for groups who can't decide where to eat
Built With
- claude
- firebase
- groq
- nextjs
- react
- tailwind
- typescript

Log in or sign up for Devpost to join the conversation.