🗳️ Pollux

Create a poll, add choices (separated with "|")

🔗 URLs calculated from choices 📡 Results update in real-time 🕐 Entries cleared every 4h

How the hash works

Choices are encoded in the URL hash using lz-string compression. A UUIDv7 identifies each poll.

const hash = LZString.compressToEncodedURIComponent(
    Bun.randomUUIDv7() + "|Yes|No|Maybe"
);

Custom colors

Append a hex color in brackets after a choice to set its button color.

const hash = LZString.compressToEncodedURIComponent(
    Bun.randomUUIDv7() + "|Yes[#F00000]|No[#0F0000]|Maybe[#00F000]"
);

How dynamic polls work

Dynamic polls use a round-based system. The hash contains only a UUIDv7 — choices are managed server-side via API. Each round (step) has its own set of choices.

🎯 https://pollux.gouz.dev/dynamic-vote#<uuid> — single vote per round 🎯 https://pollux.gouz.dev/dynamic-many#<uuid> — unlimited votes per round 📊 https://pollux.gouz.dev/dynamic-result?uuid=<uuid>&step=<N> — results for a round

End-to-end flow

1. The admin creates a session by generating a UUIDv7 (GET /api/uuid).

2. The admin sends choices for a round via POST /api/dynamic/:uuid/step. This broadcasts the round to all connected voters in real-time.

3. Voters open dynamic-vote#<uuid> or dynamic-many#<uuid> — the choices appear instantly via WebSocket.

4. Each vote is encoded as choice = 100 × step + index (e.g., step 0: indices 0,1,2…; step 1: 100,101,102…). The server tracks which step each vote belongs to.

5. Results are viewed at dynamic-result?uuid=<uuid>&step=<N> and update in real-time as votes come in.

6. When the admin advances to the next round, all voters receive the new choices and can vote again.

# 1. Generate a UUID
curl https://pollux.gouz.dev/api/uuid
# 0194f1e0-7a5a-7b00-9a8c-3e2a1b0c0d0e

# 2. Set round 0
curl -X POST https://pollux.gouz.dev/api/dynamic/0194f1e0-.../step \
  -H "Content-Type: application/json" \
  -d '{"step":0,"choices":["Yes","No","Maybe"]}'

# 3. Share with voters
# https://pollux.gouz.dev/dynamic-vote#0194f1e0-...

# 4. Advance to round 1
curl -X POST https://pollux.gouz.dev/api/dynamic/0194f1e0-.../step \
  -d '{"step":1,"choices":["Easy","Hard"]}'

# 5. View results for round 0
curl https://pollux.gouz.dev/api/vote/0194f1e0-...?step=0

Admin panel

The standalone file test-dynamic.html provides an admin UI to drive a dynamic poll session: generate a UUID, broadcast rounds with predefined choices, and preview results side-by-side.

// test-dynamic.html — core logic
const sendStep = async (step, choices) => {
  const uuid = $uuid.value.trim();
  await fetch(`/api/dynamic/${uuid}/step`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ step, choices }),
  });
  $frame.src =
    `/dynamic-result?uuid=${uuid}&step=${step}`;
};

Open test-dynamic.html in your browser (dev: from the project root; prod: serves as a static file). Enter a UUID or generate one, then click a question to broadcast it. Voters connected to dynamic-vote#<uuid> see the choices appear instantly.

API

GET /api/uuid

Generate a UUIDv7 identifier for a new poll.

# Returns a plain text UUIDv7
curl https://pollux.gouz.dev/api/uuid
# 0194f1e0-7a5a-7b00-9a8c-3e2a1b0c0d0e

POST /api/vote

Cast a vote for a specific choice in a poll.

curl -X POST https://pollux.gouz.dev/api/vote \
  -H "Content-Type: application/json" \
  -d '{"uuid":"0194f1e0-...","choice":0}'

GET /api/vote/:uuid

Retrieve the current results for a poll.

curl https://pollux.gouz.dev/api/vote/0194f1e0-...
# {"result":[{"choice":0,"total":5},{"choice":1,"total":3}]}

GET /api/flush/:uuid

Delete all votes for a poll.

curl -X GET https://pollux.gouz.dev/api/flush/0194f1e0-...

WS /ws?uuid=

Real-time results via WebSocket. Receives the same payload as GET /api/vote/:uuid on each vote.

const ws = new WebSocket("wss://pollux.gouz.dev/ws?uuid=0194f1e0-...");
ws.onmessage = (e) => console.log(JSON.parse(e.data));
// {"result":[{"choice":0,"total":6},{"choice":1,"total":3}]}

GET /api/vote/:uuid?step=N

Retrieve results filtered to a specific round (step).

curl https://pollux.gouz.dev/api/vote/0194f1e0-...?step=0
# {"result":[{"choice":0,"total":3},{"choice":1,"total":2}]}

POST /api/dynamic/:uuid/step

Set choices for a round. Broadcasts to all connected voters in real-time.

curl -X POST https://pollux.gouz.dev/api/dynamic/0194f1e0-.../step \
  -H "Content-Type: application/json" \
  -d '{"step":0,"choices":["Yes","No","Maybe"]}'

GET /api/dynamic/:uuid/step?step=N

Retrieve choices for a specific round.

curl https://pollux.gouz.dev/api/dynamic/0194f1e0-.../step?step=0
# {"step":0,"choices":["Yes","No","Maybe"]}

WS /ws/dynamic?uuid=

WebSocket for dynamic polls. Sends typed messages: {"type":"step",...} when the admin sets a round, {"type":"result",...} on each vote.

const ws = new WebSocket("wss://pollux.gouz.dev/ws/dynamic?uuid=0194f1e0-...");
ws.onmessage = (e) => console.log(JSON.parse(e.data));
// {"type":"step","step":0,"choices":["Yes","No","Maybe"]}
// {"type":"result","step":0,"result":[{"choice":0,"total":3}]}