Every sprint refinement meeting seems to face the same unnecessary friction:
- "Wait, which planning poker tool are we using today?"
- "I forgot my password, let me reset it..."
- "Can you invite me to the organization first?"
- "Wait, my screen hasn't updated yet—did everyone vote?"
After watching our engineering team lose 10-15 minutes of productive discussion every single week just getting everyone into the same voting room, I decided to build SwiftPoker.
Our core design principle was simple: Zero friction, sub-100ms real-time sync, and no mandatory logins for voters.
Here is a breakdown of how we architected the system using Next.js (App Router) and WebSockets.
1. The Real-Time Problem: WebSockets over HTTP Polling
In a live planning session, instant feedback is everything. When the facilitator clicks "Reveal Cards", every participant's screen must flip in unison.
Why not polling?
Polling at 500ms intervals generates unnecessary HTTP overhead and creates a noticeable, clunky lag when voters are on mobile devices or slower connections.
The WebSocket Solution
We used Pusher Channels for real-time pub/sub. Each planning poker room operates on a dedicated channel:
// Client-side subscription inside room hook
const channel = pusherClient.subscribe(`room-${roomId}`);
channel.bind('player-voted', (data: { userId: string }) => {
markPlayerVoted(data.userId);
});
channel.bind('reveal-votes', (results: RoundResults) => {
flipCardsAndShowMetrics(results);
});
By decoupling state broadcast from page reloads, screen sync is instantaneous with imperceptible latency.
2. Preventing "Client-Side Cheating" with Blind Voting
A critical aspect of planning poker is unbiased estimation. If an engineer sees that the Tech Lead voted an 8 or a 13 before casting their own vote, an anchoring bias occurs.
If we broadcasted the vote value over WebSockets immediately upon voting, any developer could open DevTools > Network > WS and inspect incoming payloads.
How we solved it:
- When a player selects a card, the client sends a
POSTrequest to the backend with their encrypted/hashed vote. - The WebSocket channel broadcasts a minimalist payload:
{ "userId": "usr_abc123", "hasVoted": true }
- The actual values remain secure on the server until the facilitator triggers the
reveal-votesendpoint. - Only upon reveal does the server compile the results, calculate metrics (consensus percentage, mode, average), and broadcast the full payload to all clients.
3. Eliminating Layout Shift in Next.js
Our landing page features an interactive room manager allowing users to either Create a Room (with custom presets: Fibonacci, T-Shirt, Hours) or Join an Existing Room.
Initially, conditional unmounting in React caused slight Cumulative Layout Shift (CLS):
// ❌ Avoid: Causes layout flicker and height recalculations
{activeTab === 'create' ? <CreateRoomForm /> : <JoinRoomForm />}
We refactored this to keep both views mounted, toggling visibility purely through CSS utility classes with synchronized element heights:
// ✅ Smooth: No DOM thrashing or height shifts
<div className={activeTab === 'create' ? 'block' : 'hidden'}>
<CreateRoomForm />
</div>
<div className={activeTab === 'join' ? 'block' : 'hidden'}>
<JoinRoomForm />
</div>
4. Zero Friction for the Team
The most important product feature isn't code—it's UX empathy:
- No registration required for voters: The room creator shares a link or displays a QR code.
- Teammates open the link on desktop or mobile, enter their name, and immediately see their voting deck.
- Spectator mode toggle: Observers (Scrum Masters, Product Managers, or guests) can join without skewing the team's consensus statistics.
Tech Stack Summary
- Frontend / Backend: Next.js 14+ (App Router, Server Actions)
- Styling: Tailwind CSS & Lucide Icons
- Real-Time Engine: WebSockets via Pusher
- Hosting: Vercel
If you want to test it with your agile team during your next refinement, it is live and free to use:
👉 https://www.swiftpoker.pro
I would love to hear feedback on the architecture and overall UX from fellow developers! What essential features does your team rely on during sprint planning?
Top comments (0)