# voting-system > Implements real-time audience voting and scoring systems for debates. Use this skill when building vote collection, tallying, result visualization, or integrating voting with the gamification system. - Author: Daniel Finn - Repository: Danservfinn/Eris - Version: 20251221121635 - Stars: 0 - Forks: 0 - Last Updated: 2026-02-07 - Source: https://github.com/Danservfinn/Eris - Web: https://mule.run/skillshub/@@Danservfinn/Eris~voting-system:20251221121635 --- --- name: voting-system description: Implements real-time audience voting and scoring systems for debates. Use this skill when building vote collection, tallying, result visualization, or integrating voting with the gamification system. --- # Voting System Skill This skill provides guidance for implementing the real-time voting and scoring mechanisms in Eris. ## Voting Types ### 1. Live Argument Votes - Audience votes on individual arguments as they happen - Quick tap/click interface - Aggregated in real-time with live visualization ### 2. Round Votes - Cast at the end of each round - Determines round winner - Influences final score ### 3. Final Verdict - Post-debate overall vote - Weighted by engagement (viewers who watched full debate count more) - Determines official winner ## Data Models ```typescript interface Vote { id: string; debateId: string; roundNumber?: number; argumentId?: string; voterId: string; votedForId: string; // debater who received the vote voteType: 'argument' | 'round' | 'final'; weight: number; // based on viewer engagement createdAt: Date; } interface VoteTally { debateId: string; debater1Votes: number; debater2Votes: number; totalVotes: number; lastUpdated: Date; } ``` ## Real-Time Architecture ``` ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ Viewer │────▶│ Socket.io │────▶│ Redis │ │ Client │ │ Server │ │ Pub/Sub │ └─────────────┘ └──────┬──────┘ └──────┬──────┘ │ │ ▼ ▼ ┌─────────────┐ ┌─────────────┐ │ PostgreSQL │◀────│ Worker │ │ (persist) │ │ (batch) │ └─────────────┘ └─────────────┘ ``` ## Implementation Guidelines ### Vote Submission ```typescript async function submitVote(vote: VoteInput): Promise { // 1. Validate voter eligibility const canVote = await checkVoterEligibility(vote.voterId, vote.debateId); if (!canVote) throw new VoteError('Not eligible to vote'); // 2. Check for duplicate votes (same argument/round) const existing = await findExistingVote(vote); if (existing) throw new VoteError('Already voted'); // 3. Calculate vote weight const weight = calculateVoteWeight(vote.voterId, vote.debateId); // 4. Persist to Redis for real-time await redis.lpush(`votes:${vote.debateId}`, JSON.stringify({...vote, weight})); // 5. Broadcast update await broadcastVoteUpdate(vote.debateId); return { success: true, newTally: await getTally(vote.debateId) }; } ``` ### Vote Weight Calculation ```typescript function calculateVoteWeight(userId: string, debateId: string): number { const watchTime = getWatchTime(userId, debateId); const totalDuration = getDebateDuration(debateId); const watchPercentage = watchTime / totalDuration; // Full weight for 80%+ watch time if (watchPercentage >= 0.8) return 1.0; // Scaled weight for partial viewing if (watchPercentage >= 0.5) return 0.7; if (watchPercentage >= 0.25) return 0.4; return 0.2; } ``` ### Real-Time Tally Updates ```typescript // Batch updates every 500ms to prevent UI thrashing const TALLY_UPDATE_INTERVAL = 500; function setupTallyBroadcast(debateId: string) { setInterval(async () => { const tally = await calculateCurrentTally(debateId); io.to(`debate:${debateId}`).emit('vote:tally_update', tally); }, TALLY_UPDATE_INTERVAL); } ``` ## Anti-Fraud Measures 1. **Rate Limiting** - Max 1 vote per argument/round per user 2. **Account Age** - New accounts have reduced vote weight 3. **Engagement Requirement** - Must watch minimum time to vote 4. **IP Tracking** - Flag suspicious voting patterns 5. **Captcha** - For high-stakes debates ## Visualization ### Live Vote Bar - Horizontal bar showing real-time split - Smooth animations (CSS transitions) - Pulse effect on new votes ### Vote Momentum - Show voting "momentum" (rate of change) - Highlight comebacks and swings - Engagement indicator ## Examples ### Vote Animation Trigger ```typescript function animateVoteReceived(debaterId: string, voteCount: number) { const element = document.querySelector(`[data-debater="${debaterId}"]`); element.classList.add('vote-pulse'); updateVoteDisplay(debaterId, voteCount); setTimeout(() => element.classList.remove('vote-pulse'), 300); } ``` ## Best Practices 1. Optimistic UI updates with rollback on failure 2. Batch database writes (Redis -> PostgreSQL every 5s) 3. Cache tallies aggressively, invalidate on vote 4. Graceful degradation if real-time fails 5. Accessibility: announce vote changes for screen readers