A minimalist Mentimeter clone for anonymous live voting via WebRTC Peer-to-Peer.
β
Peer-to-Peer - No server or backend required
β
Anonymous Voting - No data collection or tracking
β
Live Synchronization - Real-time vote transmission via WebRTC
β
Simple Configuration - Define votings via Markdown
β
GitHub Pages Compatible - Static hosting at no cost
β
Timer-based - Results displayed after configurable countdown
β
Manual Start - Host controls when voting begins
β
Reset Function - Run votings multiple times
Live Demo: https://saplaum.github.io/VSMeter/
git clone https://github.com/saplaum/VSMeter.git
cd VSMeter
npm installnpm run devSince GitHub Actions runners are not available, deploy manually:
# Build the project
npm run build
# The dist folder is generated
# Push dist folder to gh-pages branch (see deployment section below)Important: Adjust the base URL in vite.config.js:
export default defineConfig({
base: '/YOUR-REPO-NAME/', // e.g. '/VSMeter/'
// ...
})# Deploy to public GitHub Pages
./publish-public.sh# Build the project
npm run build
# Navigate to dist folder
cd dist
# Initialize git and push to gh-pages branch
git init
git add -A
git commit -m 'deploy'
git branch -M gh-pages
git remote add origin https://github.com/YOUR-USERNAME/VSMeter.git
git push -f origin gh-pages
cd ..Remove dist from .gitignore, commit it, and configure GitHub Pages to serve from /dist folder on main branch.
Create a new Markdown file in public/votings/:
---
id: voting3
title: "Your question?"
description: "Short description"
delaySeconds: 30
options:
- label: "Option 1"
emoji: "π"
- label: "Option 2"
emoji: "π"
- label: "Option 3"
emoji: "π€·"
---Then add the ID in src/composables/useVotingConfig.js:
const votingIds = ['voting1', 'voting2', 'voting3'];- Open the landing page
- Click "Start as Host" for a voting
- Share the generated link with participants
- Click "Start Voting" button to begin the countdown
- Results are displayed after the timer expires
- Optional: Reset voting to start a new round
- Open the link shared by the host
- Select your option
- You can change your vote until the timer expires
- Results are shown after the timer completes
βββββββββββββββ
β Host β ββββ PeerJS WebRTC
β (Browser) β
ββββββββ¬βββββββ
β
βββββ΄ββββ
β β
ββββΌβββ ββββΌβββ
β P1 β β P2 β Participants
βββββββ βββββββ
- Host = Coordinator, aggregates votes, manages timer
- Participants = Connect directly to host via WebRTC
- No central database = Everything runs in the browser
- Frontend: Vue 3 (Composition API)
- Build Tool: Vite 5
- Styling: Tailwind CSS 3 (Custom Mentimeter Dark Theme)
- WebRTC: PeerJS 1.5
- Routing: Vue Router 4
- Markdown: js-yaml (YAML frontmatter parser)
VSMeter/
βββ public/votings/ # Voting configurations (Markdown)
βββ src/
β βββ components/
β β βββ landing/ # Landing page components
β β βββ vote/ # Participant components
β β βββ shared/ # Shared components
β βββ composables/ # Vue Composables (WebRTC, Timer, Config)
β βββ utils/ # Utilities (roomId Generator, Constants)
β βββ views/ # Route Views (Landing, Host, Vote)
β βββ router/ # Vue Router
βββ .github/workflows/ # GitHub Actions (optional)
In the voting Markdown file:
delaySeconds: 45 # Seconds until results are visibleColors in tailwind.config.js:
colors: {
'vs-dark': '#000000', // Background
'vs-bar-base': '#3B82A0', // Bar dark blue
'vs-bar-mid': '#5DADE2', // Bar medium blue
'vs-bar-top': '#A8E6FF', // Bar light blue
}npm run dev # Start dev server
npm run build # Production build
npm run preview # Preview buildWebRTC debug logs are enabled. In browser console:
// Host
console.log('Room ID:', roomId.value)
console.log('Votes:', votes.value)
// Participant
console.log('My Vote:', myVote.value)
console.log('Results:', results.value)- Check if host is online
- Check room ID in link
- Check browser console for errors
- Try different browser (Chrome/Firefox recommended)
- Wait until timer expires (host view shows countdown)
- Check if host is still connected
- Refresh the page
- Check if Markdown files are in
public/votings/ - Check YAML frontmatter syntax
- Check
useVotingConfig.jsif IDs are registered
WebRTC messages exchanged between host and participants:
VOTE/VOTE_UPDATE- Participant β HostSTATE_UPDATE- Host β Participants (vote count, participant count)TIMER_UPDATE- Host β Participants (seconds remaining, isActive)TIMER_START- Host β Participants (manual start signal)RESULTS- Host β Participants (final results object)RESET- Host β Participants (clear session)
Three PI Planning votings are configured:
- Voting 1: Overall PI success rating (5-point scale: π to π)
- Voting 2: PI experience satisfaction (5-point scale: π to π)
- Voting 3: Confidence in achieving upcoming PI plans (5-point scale: π° to π)
Contributions welcome! Please:
- Fork the repo
- Create a feature branch
- Commit your changes
- Push and create a pull request
- Multiple-select votings
- Free text inputs
- Export as PNG/CSV
- QR code for vote link
- Pause/resume voting
- Dark/Light theme toggle
- Optional: Firebase backend for persistence
- Voting history
- Multi-host support
MIT License
Inspired by Mentimeter
Built with Vue.js, Tailwind CSS, and PeerJS
Made with β€οΈ for anonymous voting