🗳️ Pollux
Create a poll, add choices (separated with "|")
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}]}