A real-time, synchronized polling application for hybrid team meetings that runs on an internal Linux server. Perfect for pulse checks, team surveys, and anonymous feedback during meetings.
- Real-time synchronization - Questions appear simultaneously for all participants
- Anonymous responses - No tracking of individual users
- Hybrid meeting support - Works on office WiFi and VPN
- Multiple question types - Multiple choice, Yes/No, rating scales, and text responses
- Live results - See responses update in real-time on the admin dashboard
- Export capabilities - Download results as CSV or JSON
- Mobile-friendly - Works seamlessly on phones, tablets, and laptops
- No external dependencies - Everything runs locally on your server
- Node.js v18 or higher
- At least 512MB RAM
- 1GB disk space
- Clone or download the application
cd /path/to/teamPoll- Install dependencies
npm install- Configure environment variables
Copy the example environment file:
cp .env.example .envEdit .env and set your admin password:
ADMIN_PASSWORD=your-secure-password-here
PORT=3000
SESSION_SECRET=your-secret-key-change-this
NODE_ENV=production
IMPORTANT: Change the default admin password before running in production!
- Start the server
npm startFor development with auto-reload:
npm run dev- Access the application
- Admin interface:
http://localhost:3000/admin - Or use your server's IP:
http://[server-ip]:3000/admin
-
Login to Admin Interface
- Navigate to
http://[server-ip]:3000/admin - Enter your admin password
- Navigate to
-
Create New Poll
- Click "Create New Poll"
- Add 6-8 questions (or as many as needed)
- Choose question types:
- Multiple Choice (2-5 options)
- Yes/No
- Rating Scale (e.g., 1-5 or 1-10)
- Short Text
- Click "Save & Start Poll"
-
Share with Participants
- Copy the participant URL
- Share via Teams chat or display QR code
- Example:
http://[server-ip]:3000/poll/poll-2025-11-03-abc123
-
Control the Poll
- Click "Reveal Question 1" to show first question
- Watch responses come in real-time
- Discuss results with team
- Click "Reveal Question 2" for next question
- Repeat for all questions
-
End Poll
- Click "End Poll" when finished
- Export results as CSV or JSON if needed
-
Join the Poll
- Click the URL shared by facilitator
- Or scan QR code on your phone
-
Wait for Questions
- You'll see "Waiting for facilitator..." screen
-
Answer Questions
- Questions appear automatically when facilitator reveals them
- Select your answer
- Click "Submit"
- Wait for next question
-
Thank You
- After the last question, you'll see completion message
Edit .env file:
ADMIN_PASSWORD=changeme # Admin interface password
PORT=3000 # Server port
SESSION_SECRET=secret-key # Session encryption key
NODE_ENV=production # Environment (production/development)
Edit config/config.json:
{
"port": 3000,
"max_participants": 100,
"session_timeout_minutes": 120,
"auto_delete_polls_after_days": 30,
"rate_limit_window_ms": 60000,
"rate_limit_max_requests": 10
}Open port 3000 (or your configured port) on your server:
# For UFW (Ubuntu)
sudo ufw allow 3000/tcp
# For firewalld (RHEL/CentOS)
sudo firewall-cmd --permanent --add-port=3000/tcp
sudo firewall-cmd --reloadOffice WiFi:
- Participants:
http://[server-ip]:3000/poll/[session-id]
VPN:
- Ensure VPN allows access to internal network
- Use same URL as office WiFi
Custom Domain (Optional):
- Set up nginx/Apache reverse proxy
- Use:
http://polls.yourcompany.local/poll/[session-id]
Question 1: How satisfied are you with our current sprint process?
Type: Rating Scale (1-5)
Question 2: What's your biggest blocker right now?
Type: Multiple Choice
Options: Time constraints, Technical issues, Unclear requirements, Team communication, None
Question 3: Do you feel you have the resources you need?
Type: Yes/No
Question 4: Any additional comments?
Type: Text
Question 1: Was today's meeting productive?
Type: Rating Scale (1-10)
Question 2: Should we schedule a follow-up?
Type: Yes/No
Question 3: What should we discuss next time?
Type: Text
- Data is stored in SQLite database at
data/polls.db - Automatically created on first run
- No manual database setup required
- Old completed polls are automatically deleted after 30 days
- Configure in
config/config.json:auto_delete_polls_after_days
Delete specific poll:
- Use admin interface "Delete" button
- Or manually delete from database
Backup database:
cp data/polls.db data/polls.db.backup- No user identification stored
- No IP address logging for responses
- No cookies that identify users
- Responses cannot be traced back to individuals
- Password-protected admin interface
- Password stored in environment variable
- Change default password immediately
- Runs on internal network only
- No external API calls
- No data leaves your server
- HTTPS not required for internal network (but recommended for production)
- Go to admin dashboard
- Click on completed poll
- Click "Export as CSV"
- File downloads:
poll-[session-id].csv
Format:
Question Index,Question Text,Answer,Count
0,"How satisfied are you?","Very satisfied",5
0,"How satisfied are you?","Satisfied",3
1,"Any concerns?","Yes",2- Go to admin dashboard
- Click on completed poll
- Click "Export as JSON"
- File downloads:
poll-[session-id].json
Format:
[
{
"question_id": 1,
"question_index": 0,
"question_text": "How satisfied are you?",
"question_type": "multiple_choice",
"options": ["Very satisfied", "Satisfied", "Neutral", "Unsatisfied"],
"total_responses": 8,
"breakdown": {
"Very satisfied": 5,
"Satisfied": 3
}
}
]Error: Port already in use
Solution: Change PORT in .env file or stop other service using port 3000
Error: Cannot find module
Solution: Run `npm install` again
Check firewall:
sudo ufw statusCheck server is running:
ps aux | grep nodeCheck from participant device:
ping [server-ip]
curl http://[server-ip]:3000/poll/test-sessionCheck WebSocket connection:
- Open browser console (F12)
- Look for "Connected to server" message
- Check for error messages
Refresh the page:
- Admin: Use browser refresh
- Results page: Click "Refresh" button
Reset database:
rm data/polls.db
npm start # Database will be recreatedCheck database:
sqlite3 data/polls.db
.tables
.quit- Supports up to 100 simultaneous participants (configurable)
- Tested with 50+ concurrent users
- Response time < 500ms for submissions
- Real-time updates < 1 second latency
For larger teams (100+):
- Increase server RAM to 1GB+
- Adjust
max_participantsin config - Consider adding Redis for session management
teamPoll/
├── server/
│ ├── index.js # Main server
│ ├── routes/
│ │ ├── admin.js # Admin API routes
│ │ └── poll.js # Poll API routes
│ ├── socket/
│ │ └── handlers.js # WebSocket handlers
│ ├── models/
│ │ ├── poll.js # Poll data model
│ │ └── response.js # Response data model
│ └── db/
│ └── database.js # Database setup
├── public/
│ ├── admin/ # Admin interface
│ ├── poll/ # Participant interface
│ └── results/ # Results display
├── config/
│ └── config.json # Configuration
├── data/ # Database files (auto-created)
├── .env # Environment variables
├── package.json
└── README.md
npm run dev # Auto-restarts on file changesManual testing checklist:
- Admin can login
- Admin can create poll with different question types
- Participant can join via URL
- Questions reveal correctly
- Responses submit successfully
- Real-time updates work
- Multiple participants can submit simultaneously
- Results display correctly
- CSV export works
- JSON export works
- Poll can be ended
- Works on mobile devices
# Edit .env
NODE_ENV=production
ADMIN_PASSWORD=very-secure-password-hereInstall PM2:
npm install -g pm2Start with PM2:
pm2 start server/index.js --name team-poll
pm2 save
pm2 startup # Follow instructions to enable auto-startMonitor:
pm2 status
pm2 logs team-poll
pm2 restart team-pollNginx configuration:
server {
listen 80;
server_name polls.yourcompany.local;
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}Use Let's Encrypt with certbot:
sudo certbot --nginx -d polls.yourcompany.localAll admin endpoints require Authorization: Bearer [admin-password] header.
POST /api/admin/auth
- Verify admin password
- Body:
{ "password": "string" }
POST /api/admin/polls
- Create new poll
- Body:
{ "questions": [...] }
GET /api/admin/polls
- Get all polls
GET /api/admin/polls/:sessionId
- Get specific poll
POST /api/admin/polls/:sessionId/start
- Start poll
POST /api/admin/polls/:sessionId/end
- End poll
GET /api/admin/polls/:sessionId/results
- Get poll results
GET /api/admin/polls/:sessionId/export/csv
- Export as CSV
GET /api/admin/polls/:sessionId/export/json
- Export as JSON
GET /api/poll/:sessionId
- Get poll info
POST /api/poll/:sessionId/respond
- Submit response
- Body:
{ "question_id": number, "answer": string }
GET /api/poll/:sessionId/results
- Get results (public)
Client → Server:
admin_join- Admin joins sessionjoin_session- Participant joinsstart_poll- Start pollreveal_question- Reveal questionclose_question- Close questionsubmit_response- Submit answerend_poll- End pollrequest_results- Get current results
Server → Client:
admin_joined- Admin joined successfullysession_joined- Participant joinedpoll_started- Poll startedquestion_revealed- New question visiblequestion_closed- Question closedresponse_submitted- Response recordedresponse_received- New response (admin only)participant_count- Participant count updatepoll_ended- Poll endederror- Error occurred
Q: Can participants see other people's answers? A: No, all responses are anonymous and private.
Q: Can participants skip ahead to future questions? A: No, they only see the current question revealed by the facilitator.
Q: Can participants change their answers? A: No, once submitted, answers are final.
Q: How many participants can join?
A: Default is 100, configurable in config.json.
Q: Do participants need to create an account? A: No, participants just click the link and answer.
Q: Can I reuse the same poll session? A: No, create a new poll for each meeting. Old polls are saved for reference.
Q: Is there a mobile app? A: No, but the web interface is fully mobile-responsive.
Q: Can I customize the branding?
A: Yes, edit the CSS files in public/*/styles.css.
For issues, questions, or feature requests:
- Check the troubleshooting section above
- Review server logs:
pm2 logs team-pollor console output - Check browser console for errors (F12)
MIT License - See LICENSE file for details
Built with:
- Express.js - Web framework
- Socket.io - Real-time communication
- SQLite (better-sqlite3) - Database
- Vanilla JavaScript - Frontend
Note: This application is designed for internal network use. For internet-facing deployments, additional security measures are recommended.