This is a submission for Weekend Challenge: Passion Edition
What I Built
Pollux is an anonymous, real-time tracker of Nigerian political sentiment. Pick any of 52 politicians, national or state level, and vote support, undecided, or oppose. Leave a short comment saying why. Then watch the tally move live. See how the country breaks down across the six geopolitical zones covering all 36 states and the FCT, and read a neutral, AI-generated briefing on who the person is and what is being said about them right now.
Pollux was just the right answer to the passion angle poised in the challenge description. Nigerian politics does not do lukewarm. Online political discourse is loud and heated but this can mean that people's intentions are hard to read. All you learn is who is shouting the loudest on Twitter. On top of that, the loudest voices have been directly or indirectly "dealt with" by offended politicians with the means to bend state institutions to their needs, so most people are reluctant to share their thoughts.
I took an idea and design by my friend Ope, who spun up a first prototype, still live at pollux-ng.netlify.app. It had a few flaws. When he shared it with me and gave me permission to take it over, this challenge became my reason to give his design the backbone it deserved: a real database, a server boundary, working AI, and honest numbers.
That last one mattered most to me. Pollux should capture the heat itself instead of the noise. No accounts, no follower counts, no badges, no clout scores. Just an anonymous vote and a short reason why, feeding a running tally you can watch move in real time. The goal isn't a "who's winning 2027" scoreboard, neither do the numbers translate to real election results.
Main features
- Vote support, undecided, or oppose on 52 national and state-level politicians, with a 60-second cooldown and the option to retract
- Anonymous 280-character comments, moderated by AI before anyone sees them
- A live leaderboard, plus a Pulse page showing the top movers today, this week, and this month
- Regional breakdowns across the six geopolitical zones, built from the state and zone each voter self-declares
- Per-politician detail pages with a 0 to 100 Passion Meter, dominant emotions, neutral supporter and critic digests, and a grounded factual briefing
- A methodology panel on every page that reads from the same constants the code enforces
- Shareable politician cards via the Web Share API, with a clipboard fallback
Demo
Live app: pollux-ng.vercel.app
The best things to watch for: the tally moving without a refresh, and a detail page with the Passion Meter and AI briefing open.
Code
akcumeh
/
poll-ux
Poll - ux is an anonymous, real-time platform for tracking Nigerian political sentiment. Users can vote, comment, and explore public opinion on politicians across all 36 states, with live rankings, regional insights, and transparent, manipulation-resistant data.
Pollux
Anonymous, real time tracker of Nigerian political sentiment. Vote support, undecided, or oppose on politicians across all 36 states, comment, and explore live rankings and regional insight. Numbers below the minimum sample are withheld, never invented, and everything a machine wrote is labeled.
Pollux started as Ope-Dada/poll-ux (@Ope_Ugo's original build, live at pollux-ng.netlify.app), announced here. This repo is my response to the Dev.to Weekend Challenge Passion Edition, built on top of Ope's original idea and data model.
Architecture
A single Vercel deployment serves both halves:
-
src/is a vanilla TypeScript + Vite frontend. Pages render as template strings, event handlers attach towindow. No framework. -
api/is the backend: Vercel Node serverless functions (cast-vote,moderate-comment,remoderate,comment-status,ai-insights,briefing,report-comment). They hold the Supabase service role key and the Gemini key, enforce vote cooldowns and…
How I Built It
Ope's first version was a static site with no boundary between the browser and the database. Votes and comments wrote straight to Supabase with the anon key. The vote data was also stored on each device that visited the site, so if you didn't vote on my phone, I'd never see your vote. Anyone with a basic idea of devtools open could clear their browser to forge unlimited votes. A "Comment posted ✓" toast fires automatically without making any network calls.
The regional numbers were also fictional. A function multiplied national support by hardcoded per-politician bias coefficients, and returned 40 + Math.floor(Math.random() * 22) whenever a politician had zero votes. The "AI insights" button copied a prompt to the clipboard and told you to paste it into any AI tool of your choice. None of that makes the design weak. It's what a first pass looks like, and I believed it was good enough that I wanted to build on it rather than start over.
One deployment, two halves
I moved the deployment from Netlify to Vercel to get serverless functions, and that split is now the backbone of the trust model. A single Vercel project serves everything. src/ is a vanilla TypeScript and Vite frontend, no framework, two runtime dependencies total (@supabase/supabase-js and @google/genai).
api/ holds five Vercel Node functions: cast-vote, moderate-comment, ai-insights, briefing, and report-comment. The browser only ever holds the Supabase anon key, and Row Level Security means it can only read. Every write, a vote, a retraction, a comment, a report, goes through api/, which holds the service-role key. The service-role key and the Gemini key never touch client code.
Rate limits (60-second vote cooldown, 150 actions a day, 20 reports an hour) and input validation are enforced server-side and returned as proper HTTP 429s with retryAfterSeconds. Postgres triggers recount the vote aggregates on every insert, update, and delete. Realtime pushes fresh tallies to every open tab, making the numbers move without you doing anything. The schema is versioned in supabase/migrations now, something lacking in the original.
Trust by default
AI analysis only runs when a politician gets at least 10 votes, while regional data is pulled from the state and zone each user can optionally self-declare. The Pulse page's top movers are counted from server-side timestamps to match the rankings on every device, which is something Ope's site lacked.
On the database side, I set up RLS to restrict poll_comments reads to status = 'approved', so if the moderation handler ever misbehaves, say I run out of API credits, held comments still can't leak to other users. Everything generated by AI is clearly labeled: the briefing, the Passion Meter score, the emotions, both digests, each with a timestamp so you know how stale it is.
Identity for rate-limiting comes from a browser fingerprint. fingerprintJS derives a stable per-device ID from browser and hardware signals; this ID is what the 60-second cooldown and the daily caps check against. This is to enforce account uniqueness and prevent manipulation of the votes.
Gemini, the three-headed hydra
I was targeting Best Use of Google AI from the start, which meant the clipboard trick had to go, in favor of something better. With just gemini-2.5-flash via @google/genai, comment moderation handles English, Nigerian Pidgin, Yoruba, Igbo, and Hausa, plus whatever mix of the five, or slang a commenter throws at it. The prompt judges meaning, not keywords, classifying each comment as clean / abusive / spam / incitement through structured JSON output at temperature 0.2. Strong political disagreement is explicitly clean.
Debate insights kick in once a politician has 5 approved comments. Gemini reads up to the 80 most recent, each tagged with the commenter's stance, and produces a 0 to 100 temperature score, 2 to 4 dominant emotions, and two short neutral digests: what supporters argue, what critics argue, 80 words each. It's prompted to read sentiment the way a Nigerian reader would ("e no easy" can be praise, "God abeg" can be exhaustion) and to say plainly when a side is missing.
Grounded briefings run last: up to 130 words per politician, generated with Google Search grounding, covering their current role, recent news, live controversies, the debate around them. The prompt forbids praising, condemning, predicting election outcomes, or inventing approval numbers.
Insights are recomputed whenever the approved comment count is updated. Briefings are updated every hour, but if you want a fresh update right away, after one minute a refresh button appears. This way, if any generation fails, the last good version serves instead of an empty screen.
Updates I made
Cache invalidation lived up to its reputation. My git log for the weekend includes "fix: briefing now in hourly buckets," "fix: rejected comments persisting," "fix: pending comment state + cooldown.".
The other one was moderation. Five languages plus Nigerian slang is a problem I can't solve in a weeked so I just trusted Gemini to make the judgment calls following the system prompt provided.
I also moved the comment thread above the regional panel on the detail page. The comments are where the votes get their context; the regional chart is secondary.
Connection to the Theme
The theme is Passion, and it shaped what I built more directly than any feature list suggests. The Passion Meter exists because of it: a gauge on every detail page that doesn't tell you who is winning, only how much heat a debate is carrying, whether that heat comes from supporters or critics. The AI moderation and the debate digests came from the same place, because measuring passion honestly means keeping the thread readable and summarizing both sides without picking one.
Nigerians are among the most politically passionate people anywhere. We argue about governors in barbershops and every election season is a national obsession. A side project that captures that intensity without the bot armies was a fun one.
What's Next
The anonymous model is a deliberate trade-off: no login means more honest votes, and the browser fingerprint closes the easiest cheat, clearing your storage and voting again. It isn't proof of a unique human though; a fresh browser profile or a second device still gets past it. After the challenge, I want to explore:
- more sophisticated vote-integrity signals beyond cooldowns and daily ceilings
- a richer regional visualization, ideally an interactive map of the 36 states
- surfacing comment trends over time, beyond the current digest
- an admin review queue for held and reported comments, so moderation decisions are auditable
Prize Categories
Best Use of Google AI. Gemini 2.5 Flash has three distinct roles in Pollux:
- real-time comment moderation across five languages, with structured JSON classification
- debate analysis producing the Passion Meter score, dominant emotions, and neutral digests for both sides
- grounded factual briefings via Google Search grounding
Author
Thank you for reading this far! Follow me and connect with me via:
Top comments (3)
Incredible!!!
Thank you!! 😁
Wow! Congratulations on the project, it turned out amazing!