Show of Hands
Ask a room a question. They answer on their phones. The answers appear on a screen as they come in.
This page has three parts. Start here is all most people need. For your agent is for driving the poll from an AI assistant. Technical detail is everything else.
Start here
You need: the code from your confirmation email, a laptop for the screen, and your phone.
1. Open your poll
Go to https://showofhands.live/redeem
Paste your code, type a name for your poll, and press the button.
You get three links, a key, and two QR codes. If you are on a laptop, point your phone camera at the Scan to control it square: that puts the controls on your phone without typing a URL.
Keep your code. If you lose the links or the key, or you need them on a different phone or laptop, go back to https://showofhands.live/redeem and enter the same code again. It gives you the same poll back, with everything still in it. It does not open a second one and it does not cost anything. Your code works this way until your pass ends.
2. Set up the room
On your laptop, open the link ending in /screen and press F11 for full screen. A big square
code appears. That is what people scan.
On your phone, open the link ending in /control. It asks for the key. Paste it in once and it
remembers.
3. Ask your first question
On your phone, tap Write my own, type a question, add up to four answers to choose from, and tap Put it on screen.
In a hurry? Tap Ask AI instead, type a topic, and a question comes back with its answers ready for you to approve.
Tell the room to point their phone camera at the square code on the screen. They tap the link that pops up, give a name and email, and answer.
Bars grow on the screen as answers arrive.
4. Move on
Tap Next for the next question. Tap Close answers if you want the numbers to stop moving while you talk about them.
5. Finish
Press and hold Hold to end session.
6. Get your results
Go to https://showofhands.live/live/YOUR-CODE/results and enter the same key. There is a link to it on your phone controls.
You see every question with its counts, and four buttons: Results as CSV for a spreadsheet, Results as JSON, Print or save as PDF, and Attendee list as CSV for the names and emails people gave when they joined.
Not running a talk? On the page in step 1, choose In their own time instead. No laptop and no screen: add your questions, tap Open every question, and send people the answering link. They answer whenever suits, for as long as your pass lasts.
What is in your pass
A pass lasts 7 days, is bought once, and needs no account and no subscription. One pass opens one poll.
| Tier | Participants | Questions | Price |
|---|---|---|---|
| basic | 20 | 20 | USDC 1 |
| small | 100 | 20 | USDC 3 |
| medium | 500 | 30 | USDC 5 |
Bigger rooms are coming. If you need one now, email mail@richard-hobbs.com and say how many people you expect.
Three links come with every pass:
| Link | Who opens it |
|---|---|
https://showofhands.live/live/<code> | The people answering, on their phones |
https://showofhands.live/live/<code>/screen | You, on the laptop driving the big screen |
https://showofhands.live/live/<code>/control | You, on your phone |
Nobody installs anything and nobody makes an account.
Before your event
Set your phone up now, not on stage. Open the control link on the phone you will actually hold, paste the key, tap Unlock. It remembers, and it remembers each poll separately, so you can hold two of them without one overwriting the other.
Add it to your home screen so you are not hunting through tabs mid-talk.
Hard reload the screen and your phone (Ctrl+Shift+R, or Cmd+Shift+R on a Mac). A tab left open for days keeps running the version it loaded on the day you opened it. It looks fine right up until you need something new to work.
Open the screen link on the laptop and go full screen (F11, or Ctrl+Cmd+F on a Mac). The holding screen is mostly QR by design so it scans from the back of a room. It shrinks into the corner once your first question is up, and stays there for latecomers.
The screen asks the browser to keep the laptop awake. That is a request, not a guarantee, so plug the charger in and turn off sleep anyway.
Your phone controls, button by button
| Button | Does | Greyed out when |
|---|---|---|
| Back | Previous question | You are on the first |
| Next | Next question, or the first if none is showing | You are on the last |
| Close answers | Freezes voting, bars stay on screen | Nothing showing, or already closed |
| Reopen | Lets late answers in again | Answers are already open |
| Hold to end session | Closes everything, shows your closing screen | Already ended |
Close answers is the one worth knowing. Use it when you want to discuss a result without the numbers shifting under you while you talk.
Ending is a press-and-hold on purpose. There is no undo.
Adding questions
Two ways, and the difference matters.
Write my own. Type the question, choose Multiple choice or Open answer, fill in up to four options. Empty option slots are ignored, so two is fine. Then Put it on screen to publish it now, or Add to running order to queue it behind the current one.
Ask AI. Type a topic and a question comes back with its answers a few seconds later. It arrives in an approval queue with Approve and Discard, and the room cannot see it until you approve it.
That asymmetry is deliberate. What you type goes up when you tap the button, because you read it as you wrote it. What a model wrote gets a second pair of eyes before a room sees it.
A count appears next to your poll code whenever something is waiting for approval. Tap it to jump to the queue.
Polls people answer in their own time
Choose In their own time when you open the poll and it works differently: no presenter, no big screen, every question open at once. Good for feedback after an event or a survey round a team.
Two buttons instead of Back and Next:
| Button | Does |
|---|---|
| Open every question | Opens the whole set. Share the answering link and answers come in |
| Close the poll to new answers | Stops new answers. Your results stay readable |
People see the whole set on one page with a progress bar, answer in any order, and can close the tab and come back. Their answers are remembered on their own device, so returning shows what they already sent rather than asking again.
Your pass expiry is the deadline. After it, answers are refused whether or not you remembered to close the poll.
You can add questions mid-run. People who already finished see the new one next time they open the link.
Offering something for finishing
People finish a self-paced poll when there is a reason to. On your phone controls there is Offer something for finishing: type what you are offering, save it, and mention it when you send the link out.
It is shown only to somebody who has answered every question, with a box for their email, already filled in with the address they joined with. They can change it, so a work address for the poll and a personal one for the reward both work.
You collect the addresses from your results page: Who finished, as CSV or as JSON. There are no emails sent to you one by one, and nothing is sent to them either.
Whatever you offer is yours to honour. We carry the message and hand you the list; we hold nothing and promise your audience nothing.
Turning it off is the same box, emptied and saved.
Your results
https://showofhands.live/live/<code>/results, unlocked with the same key. There is a link to it on your phone
controls.
| Export | What it is |
|---|---|
| Results as CSV | One row per answer: question number, question, type, answer, votes, share |
| Results as JSON | For feeding into something else |
| Print or save as PDF | The page without the buttons, for a report or a slide |
| Who joined, as CSV | Name, email and join time for everyone who joined |
| Who finished, as CSV | The same, narrowed to people who completed a self-paced poll, with the address they gave |
| Who finished, as JSON | The same list, for feeding into something else |
Everything is made in your browser and downloaded to your device. Nothing is emailed anywhere.
Typed answers are grouped before counting, ignoring capitals and trailing full stops, so
Booking flights, booking flights. and Booking Flights count as one answer with three votes.
Summarising what people typed
Every open-answer question with at least four answers gets Summarise what the room said: three to five themes with rough proportions, instead of twenty typed replies you will never re-read. It appears under the chart and is included in the CSV, JSON and PDF exports.
It is saved once made, so looking again costs nothing. Regenerate forces a fresh one.
Below four answers it refuses. That floor is not adjustable: a summary of two or three replies in a small room can point at who wrote them.
If something goes wrong
| Symptom | What it means | What to do |
|---|---|---|
| Screen frozen on an old question | Dropped connection, or a laptop waking from sleep | It fixes itself within four seconds. If not, reload; nothing is lost |
| Someone's phone is stuck | Same, on their device | Lock and unlock it, or switch apps and come back |
| "That key is wrong" | Wrong or forgotten key | Type it again. Capitals do not matter |
| "too many attempts" | Ten tries a minute per network | Wait a minute. You only see this if someone is guessing |
| "This room is full" | You hit your participant limit | Nothing to do mid-talk. A larger pass next time |
| "This poll has ended" | The 7 days are up | Answers are refused from then on |
| Nobody can join | The venue is blocking scanned links | Read the URL aloud. It is short enough to type |
| Room bigger than the wifi | Saturated venue wifi | Tell them to use mobile data. Each phone uses very little |
| The big screen dies | Laptop or browser | Keep going from your phone. It shows the question and the live count |
Every answer is saved as it arrives, not held in the browser. Reloading anything loses nothing.
What your audience is promised
The join screen tells people their answers are anonymous. That is true by how the thing is built, not by policy.
Nobody can tell who answered what. Each phone makes up a throwaway code for itself and files its answers under that. It matches nothing in the list of who joined, and no record anywhere links the two. There is no export, no screen and no query that can put a name against an answer.
Their email is only ever yours. It is not readable from any browser, including yours. It comes out only in the attendee list you download, which contains no answers.
One answer per phone per question. Answering twice is quietly ignored rather than counted.
Your key opens your poll and nothing else. Every poll has its own.
For your agent
Give your agent the microphone.
Any agent can reach this poll at https://showofhands.live/mcp over MCP: put a question to a live room mid-talk,
advance it, and read the answers back, while the talk is happening. No other audience-response tool
has an endpoint an agent can drive.
You do not need any of this to run a poll. Everything above works from a phone.
Streamable HTTP, JSON-RPC over POST. GET returns a summary. Every tool except redeem_pass takes
the session key.
| Tool | Does |
|---|---|
read_manual | This document, so an agent arriving cold can work out the rest |
redeem_pass | Turn a purchased code into a poll and its links |
add_poll_question | Put a question into a running room |
advance_poll | Next or previous question |
open_all_questions | Open the whole set at once, for a self-paced poll |
close_answers | Freeze voting while you talk about a result |
end_poll | Close the session |
get_poll_results | Counts and a headcount. Never identities |
redeem_pass
| Argument | Type | Required | Notes |
|---|---|---|---|
code | string | yes | The redemption code. Case-insensitive |
title | string | yes | What the room sees while joining |
subtitle | string | no | One line under the title |
questions | array | yes | May be empty; add them later |
questions[].prompt | string | yes | Under ~120 characters stays legible on a projector |
questions[].kind | free | choice | no | Defaults to free |
questions[].options | string[] | when choice | 2 to 6 short answers |
mode | live | async | no | Defaults to live. async is the self-paced shape |
branding | object | no | See Branding below |
curl -s -X POST "https://showofhands.live/mcp" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"redeem_pass","arguments":{
"code":"MED-7K2P-QX94",
"title":"Agentic commerce",
"questions":[{"prompt":"Do you trust an agent to buy for you?","kind":"choice","options":["Yes","With limits","No"]}]}}}'
Lost the session key? Call redeem_pass again with the same code. A code that has already
opened a room returns that room, its URLs and its key, with "reopened": true, instead of an
error. It never opens a second room and it costs nothing. title is still required by the schema
but is ignored on that path, because the poll keeps the name it already has. This is what makes the
redemption code, not your stored state, the thing worth keeping for the life of the pass.
add_poll_question
| Argument | Type | Required | Notes |
|---|---|---|---|
code | string | yes | The poll code |
key | string | yes | The session key |
prompt | string | yes | The question as the room reads it |
kind | free | choice | no | Defaults to free |
options | string[] | when choice | 2 to 6 short answers |
publish | approve | now | queue | no | Defaults to approve |
publish is the important one. By default a question lands as a draft: it appears on the
presenter's phone with Approve and Discard, and the room cannot see it. That is enforced in the
database, not in the interface, so a draft cannot reach the screen even if something goes wrong
client side. Use now when the agent is yours and putting the question up live is the point.
advance_poll
| Argument | Type | Required | Notes |
|---|---|---|---|
code | string | yes | |
key | string | yes | |
direction | next | prev | no | Defaults to next |
Drafts awaiting approval are skipped. They only enter the running order once a human approves them.
open_all_questions
The self-paced control. advance_poll has no meaning without a screen; this does the job instead.
| Argument | Type | Required | Notes |
|---|---|---|---|
code | string | yes | |
key | string | yes | |
close | boolean | no | True stops new answers instead of opening the set |
Opening a set never closes a question somebody may be part way through.
close_answers
| Argument | Type | Required | Notes |
|---|---|---|---|
code | string | yes | |
key | string | yes | |
reopen | boolean | no | True reopens answering instead of closing it |
end_poll
| Argument | Type | Required |
|---|---|---|
code | string | yes |
key | string | yes |
Ends the session and switches the screen to the closing screen. The pass stays valid until it expires, so results can still be read back.
get_poll_results
| Argument | Type | Required |
|---|---|---|
code | string | yes |
key | string | yes |
Returns the count for every option on every question, plus how many people joined.
This tool cannot return a name, an email, or an individual answer, and there is no argument that makes it.
Over plain HTTP
Everything above is also reachable with the session key as a bearer token, for when there is no agent to hand:
curl -s -X POST "https://showofhands.live/api/live/<code>/questions" \
-H "Authorization: Bearer <session_key>" \
-H "Content-Type: application/json" \
-d '{"prompt":"What would you hand to an agent first","kind":"free","publish":"now"}'
| Endpoint | Method | Does |
|---|---|---|
/api/live/<code>/control?action=next|prev|close|reopen|end | POST | The phone controls |
/api/live/<code>/control?action=open_all|close_all | POST | Open or close a self-paced set |
/api/live/<code>/questions | POST | Add a question |
/api/live/<code>/pending | GET | Read drafts awaiting approval |
/api/live/<code>/pending?action=approve|discard | POST | Review a draft, body {"question_id":"..."} |
/api/live/<code>/draft | POST | Ask the model for a question, body {"topic":"..."} |
/api/live/<code>/join | POST | Join the room, body {"name":"...","email":"..."} |
/api/live/<code>/results | GET | Counts and a headcount |
/api/live/<code>/participants | GET | Who joined, who finished, and their contact address |
/api/live/<code>/offer | POST | Set the completion offer, body {"text":"..."}. Empty clears it |
/api/questions/<id>/summarise | POST | Summarise one question's typed answers |
Technical detail
Branding
The screen is unbranded by default. Nothing on it identifies the tool, which is the point: it is your event, not ours. Neither AI feature marks the screen either.
Set with the branding argument when an agent opens the poll. The web form does not offer it.
{
"accent": "#F59E0B",
"closing": {
"mode": "custom",
"heading": "Come and see us at stand 14",
"sub": "Demos every half hour.",
"cta": "Book a slot",
"url": "https://example.com/book"
}
}
closing.mode is off (a plain thank you), custom (your own call to action, shown with a QR), or
via (the VIA signup, used when VIA runs the event).
accent must be a six-digit hex and a closing URL must be http or https. Anything else is dropped,
because both end up inside the screen's own styling and markup.
How the anonymity guarantee is enforced
poll_responses.participant_id holds a throwaway token generated in the respondent's own browser.
It matches no row in poll_participants, there is no foreign key between them, and nothing
server-side holds the mapping. Verifiable at any time; this must return 0:
select count(*) from poll_responses r
where exists (select 1 from poll_participants p where p.id = r.participant_id);
poll_participants has no select policy at all, so respondent emails are unreadable from any
browser and reachable only with the service key. One answer per device per question is a unique
index, not interface logic. Participant and question caps are enforced in the database, the
participant cap under a per-session advisory lock so a room that all scans at once cannot race past
it. An expired pass is refused by the database, not by the pages.
The summariser
DeepSeek V4-Flash. It is given the answer text and nothing else: never a name, an email, a timestamp, or the token an answer was recorded against. It refuses below four answers.
Summaries are cached on the question row, so a second look makes no call.
Buying
Passes are sold in the DrHobbs Knowledge store on VIA (ERC-8004 agent 59665), which settles the
payment in USDC and emails a redemption code that looks like MED-7K2P-QX94. There is no payment
page here and this app holds no wallet.
Two ways to turn a code into a poll, doing exactly the same thing: https://showofhands.live/redeem for people,
redeem_pass for agents.
At a glance
- Base URL:
https://showofhands.live - MCP endpoint:
https://showofhands.live/mcp - This document as markdown:
https://showofhands.live/manual.md - Support: mail@richard-hobbs.com