Collect stakeholder feedback on a prototype
Share a prototype and the notes come back as screenshots in Slack, with no way to tell which page or which build they were about. The feedback widget is one script tag on the prototype: reviewers pin a comment on the exact spot, and it arrives with a screenshot and the page context already attached.
1. Turn comments on for a project
Open the project holding the prototype and go to Comments. If comments have never been enabled, you get a short explainer and an Enable comments button. Enabling switches the feature on and mints the project's API key, which ties the widget on your site back to this project.
Each project has its own key and its own inbox, so a round of feedback on one prototype never mixes with another.
2. Add the widget to the prototype
The Comments view then shows the install step with two tabs.
With Claude gives you a prompt to paste into Claude, Claude Code, or any coding assistant. It describes both script tags and where they go, which is usually the fastest route if an agent already has your repo open.
Code gives you the snippet directly. Add it just before the closing
</body> tag:
<!-- Stunt Double Feedback Widget -->
<script src="https://app.stuntdouble.io/api/feedback/widget"
data-sd-key="YOUR_PROJECT_KEY"
data-sd-url="https://app.stuntdouble.io"
defer></script>
<!-- Optional: Add html2canvas for screenshot support -->
<script src="https://cdn.jsdelivr.net/npm/html2canvas@1.4.1/dist/html2canvas.min.js" defer></script>
The attributes are:
data-sd-key(required) is your project's feedback API key. Without it the widget logs a warning and does nothing.data-sd-url(required in practice) points at Stunt Double. It must behttps; the widget refuses to run otherwise.data-sd-version(optional) pins comments to a version string you control, which is how you keep one round of feedback separate from the next. See Prototype versions below.
The second script tag is genuinely optional. If html2canvas is not already on
the page the widget fetches it itself, from a pinned version checked against its
subresource integrity hash. Including it up front just means the first
screenshot is ready sooner.
There is no package to install and no build step, so this works on a staging deploy, a preview URL, or a throwaway static build just as well as on your production site.
Confirming it works
The install screen polls while you work. Add the snippet, deploy or reload the prototype, and visit any page carrying it. Within a few seconds the screen flips from Waiting for widget connection to Widget detected and moves you on to the Comments view. No manual verification step, and nothing to paste back.
If it never flips, the two usual causes are a mistyped key and a domain that is not allowed yet. Both are covered under Control who can use it.
3. Hand it to your stakeholders
Before you send the link, decide who should be able to comment.
For an internal review, turn on Require sign in under Comments → settings. Only signed-in members of the workspace can then read or leave comments, which is the right setting for an unreleased prototype on a public staging URL. Reviewers get a Sign in to review button instead of an anonymous form: it opens a popup, they sign in to Stunt Double as normal, and the popup hands the widget a short-lived token proving workspace membership. Their replies are badged Team, and their name comes from their account rather than a form field.
The token lasts an hour, so a long review session may ask for a fresh sign-in. Because a cross-origin widget cannot read the Stunt Double session cookie, this token is the only thing that unlocks gated data: the sign-in state the widget shows is cosmetic on its own.
For a wider round, with customers or people outside the workspace, leave sign-in off and send the link. Commenting then takes three actions:
- Click the floating button in the bottom-right corner, or press
c. - Click the spot the comment is about. A pin lands there.
- Type a name, optionally an email, and the comment, then send. Escape cancels at any point.
The name and email are remembered in that person's browser, so their second comment only needs the comment itself. The email is optional and only used to notify them when someone replies to or resolves what they left.
Every submission carries more than the text: a screenshot of the page as it looked, the page URL, the pin position, viewport size, scroll offset, and basic device information. That is what makes "this bit is confusing" actionable a week later.
Keep Show comments on page enabled for a group review and existing pins are drawn on the page, so five stakeholders discuss one thread instead of filing the same note five times. Turn it off when you want independent reactions: the widget still collects everything, it just does not show anyone else's.
4. Triage what comes back
The project's Comments view is where feedback lands. It opens on the Open queue with counts across the top: total comments, how many are open, how many pages they came from, and how many are resolved.
Comments are grouped by page, because that is how you fix them. Expand a page to see every pin left on it, collapse the ones you have dealt with, or use Collapse all when the list gets long. Where the pins cluster is where the prototype is failing.
Opening a comment shows the full detail: the screenshot with the pin in place, the device and viewport it was left on, the thread of replies, and the status control. From there you can:
- Reply in the thread. If the person left an email they are notified, and the reply appears on the page for them under a Team badge.
- Change status between new, reviewed, resolved, and dismissed. Resolving notifies the author, which is what turns a round of feedback into a closed loop rather than a list nobody hears back about.
- Resolve all to clear the whole open queue at once, after a round of fixes.
Resolved and dismissed comments are filed under Resolved rather than deleted, so you can still show why a decision was made three iterations ago.
Prototype versions
Prototypes change fast, and a comment about a button you have since removed is noise unless you know which iteration it referred to. The widget hashes the page content with each comment and groups them into versions: v1 is the earliest snapshot that received a comment, and the newest is badged Latest. A page group with more than one version shows a small timeline, and selecting a version filters the group to the comments left on it.
By default the hash is computed from the rendered page, and recomputed as you
navigate a single-page app. If you would rather tie versions to your own
iterations, set data-sd-version on the script tag to a release tag, build ID,
or commit SHA. When it is set, the widget uses it verbatim and never re-hashes.
That is the cleaner option when you are showing the same prototype to a new
group each week.
Control who can use it
Your API key sits in your page source, so anyone who views source can read it. Treat it as an identifier, not a secret. The real access control is the domain allowlist, and every widget endpoint checks it.
From Comments → settings (the gear icon):
- Allowed domains is the list of hosts the widget API answers. Your project
URL is always accepted and shown as an implicit entry, so you do not need to
duplicate it. Add up to 20 more. An entry matches that exact host; use
*.example.comto cover every subdomain, which is the usual way to cover a preview deployment that gets a new URL each build. Include a port orhttp://prefix only if your site needs one. Requests from anywhere else are refused. - Allow local development (on by default) additionally permits
localhostand private network addresses, so the widget works while you build. - Require sign in restricts both reading and leaving comments to signed-in members of the owning workspace. Use it for staging environments and internal reviews.
- Toolbar visible, Show comments on page, and Keyboard shortcut
control what reviewers actually see. Turn the shortcut off if
cclashes with a shortcut of your own. - Rotate the API key to cut off an install you no longer control, such as a prototype you have stopped maintaining. The widget stops working until you update the snippet with the new key, which is the point.
The allowlist fails closed: a project with no URL and an empty allowlist serves nothing at all. The settings page warns you when that is the case, so you can tell "locked down" apart from "broken".
Workspace-level controls
Workspace admins have a ceiling above every project, under Settings → Security:
- Feedback widget switches the widget off for the entire workspace. When off, every widget endpoint returns nothing for every project in it, including sites where the key is already installed.
- A workspace-level allowed origins list narrows what any project may allow. A project can only ever be more restrictive than the workspace, never less.
Beyond the prototype
The same install works unchanged once the thing you were prototyping ships. Leave the snippet in place with sign-in off, and you have a standing feedback channel on production: people report what bothers them where it bothers them, grouped by page and version, in a queue rather than a support inbox.
Working with comments over the API
Feedback is available through the MCP server, so an assistant can triage it with
you: list_feedback (filter by status when working the new queue), get_feedback
for one item and its replies, and update_feedback_status to resolve or dismiss.
The relevant scopes are feedback:read and feedback:write. See the
MCP server page for connecting a client.
A natural pairing: read the open comments for a page, then run a checklist against that page to reproduce what your reviewers reported before deciding what to fix.
Tips
- Install it on the staging build with Require sign in on, before anyone external sees the prototype. Your team gets a low-friction way to leave notes in context, and nothing is exposed publicly.
- Ask for the email field when you actually intend to reply. Silence after a feedback round is worse than no widget.
- Set
data-sd-versionto your build ID if you iterate daily. Version grouping by content hash is good; grouping by release is unambiguous. - Resolve aggressively between iterations. The Open queue is only useful if it reflects what is genuinely outstanding, and resolved comments are still there to look back on.
- Pair it with an interview on the same prototype: actor personas show you what a careful user hits, the widget shows you what your stakeholders actually noticed.
Next steps
- Run a user interview to put persona participants through the same prototype the stakeholders reviewed.
- Write checklists that catch real regressions to turn a recurring complaint into a check that runs on every deploy.