diff --git a/README.md b/README.md index 0217c70..a2d3e05 100644 --- a/README.md +++ b/README.md @@ -1 +1,55 @@ -# P2P Poll App \ No newline at end of file +# P2P Poll App + +A peer-to-peer polling app built with [Automerge](https://automerge.org/) and Vanilla JS. No backend required — polls sync in real time across browsers using CRDTs and a WebSocket relay. + +## Features + +- **Add options** — type an option and press Enter or click "Add Option". Duplicate options (case-insensitive) are rejected with an inline error message. +- **Vote on options** — click the vote button next to any option. You can vote on multiple options, but only once per option. +- **Toggle votes** — click your vote again to remove it (the button turns green with a checkmark when you've voted). +- **Live vote counts** — options are sorted by vote count in descending order and update in real time as votes come in from other users. +- **2-minute countdown timer** — click "Start 2-min Vote" to begin a shared countdown. Voting is open before and during the countdown. Once the timer expires, all vote buttons are disabled. +- **Shared timer** — the deadline is stored in the Automerge document, so all peers see the same countdown and voting closes simultaneously for everyone. +- **Copy URL** — click "Copy URL" to copy the poll link to your clipboard. Share it with others to join the same poll. +- **P2P sync** — all state (options, votes, timer) syncs across browsers in real time with no central server handling your data. + +## How it works + +### Tech stack + +- **[Automerge](https://automerge.org/)** via `@automerge/vanillajs` — a CRDT library that lets multiple clients concurrently edit shared documents and merge changes without conflicts. +- **BroadcastChannelNetworkAdapter** — syncs between tabs in the same browser instantly via the browser's BroadcastChannel API. +- **WebSocketClientAdapter** — syncs across different browsers/devices via a WebSocket relay (`wss://sync.automerge.org`). The relay only forwards CRDT messages; it does not store or interpret your data. +- **IndexedDBStorageAdapter** — persists the document locally so it survives page refreshes. +- **Vite** — bundles the app with WebAssembly support (required by Automerge). + +### Data model + +The poll is a single Automerge document with three fields: + +```js +{ + options: string[], // list of poll options in insertion order + votes: { [option]: number }, // vote count per option + deadline: number | null // Unix timestamp (ms) when voting closes, or null +} +``` + +### URL-based sharing + +Each document gets a unique Automerge URL (e.g. `automerge:abc123...`) stored in the page's URL hash. Opening the same URL in any browser loads and syncs the same document. Creating a new poll generates a new document and updates the hash automatically. + +### Voting rules + +- Voted options are tracked in `localStorage` (keyed by document URL), so they persist across page refreshes but are isolated per browser. +- One vote per option per user. Votes can be toggled off and back on until the timer expires. +- The timer (`doc.deadline`) is set once by whoever clicks "Start 2-min Vote" and is shared via the Automerge document, so all peers use the same deadline. + +## Running locally + +```bash +pnpm install +pnpm dev +``` + +Open the printed localhost URL. To test cross-browser sync, copy the URL (including the `#automerge:...` hash) and open it in a different browser.