Holler

The Feedback button

The Feedback button is one script tag you add to your site. This page lists what the script does, so you or your coding agent can check the code against it before adding it.

<script src="/w/v1.js" data-project="your-project" defer></script>

When it shows

  • Only for people who open your site through your feedback link, which adds ?feedback=your-project to the address. It then stays on in that browser tab until the tab is closed.
  • On every page view, if you add data-mode="always" to the tag.
  • For everyone else it sends one “installed” message per tab and stops. No button, no listeners, no recording.

What it does while active

  • Adds one element, <div id="holler-feedback">, at the end of the page. The button lives in its Shadow DOM, so its styles and yours don’t mix.
  • Wraps window.fetch and XMLHttpRequest.prototype.open to notice failed requests (status 400 or above, or no response). For those it keeps the method, the address without its query string, and the status. Long random segments in the address, like invite tokens, become :token. It doesn’t read request or response bodies or headers, and passes every call through unchanged.
  • Wraps console.error and listens for error and unhandledrejection events, keeping each message (up to 500 characters). Your console output is unchanged.
  • Wraps history.pushState and replaceState, and listens for popstate and hashchange, to note page changes. Query strings are dropped and tokens replaced, as above.
  • Listens for clicks to note a short label of what was clicked, such as “Pay now button”: its accessible name, form label, placeholder or visible text, up to 60 characters. Never what was typed into a field.
  • Keeps the last 200 page changes and clicks and the last 50 errors in memory. Nothing leaves the page until the reviewer sends a note.
  • Takes a screenshot when the reviewer points at something or writes a note, and up to 12 during a spoken walkthrough (at most one every 8 seconds). It draws the page itself, so there’s no screen-sharing prompt. Text typed into fields becomes dots, and anything marked data-holler-private is blanked. To draw images and web fonts it requests them again from where your page loaded them, usually straight from the browser cache, and briefly adds a hidden, empty frame.
  • Asks for the microphone only when the reviewer taps the mic or “Talk while you use it”.
  • These wrappers and listeners stay in place until the tab is closed.

What it sends, and where

Every request goes to the Holler site the script was loaded from, under /api/f/your-project/. They carry no cookies: they’re cross-origin requests made with the browser’s default credentials setting.

WhatWhen, and where to
{"widget":"installed"} and nothing else. Holler records the time and your site’s origin, from the browser’s Origin header.Once per tab, for visitors who didn’t open the feedback linkPOST /visit
A random reviewer ID, made in the reviewer’s browserWhen the button turns onPOST /visit
The reviewer ID, to show what was fixed since their last visitWhen the button turns onGET /fixed
The note: text, optional name and reaction; page path and title; the element pointed at (label, CSS selector, position); up to 30 recent page changes and clicks; errors and failed requests from around then; window sizeWhen the reviewer sends a notePOST /items
The voice recording, if they talked. It’s transcribed, then discarded.With a spoken notePOST /items
Screenshots, as JPEGWith a notePOST /media
The reviewer ID, the issue, and “looks good” or “still broken”When a reviewer checks a fixPOST /verdict

What it stores in the browser

KeyWho gets it
holler.installedSession storageEvery visitor, so the installed message is sent once per tab. Cleared when the tab closes.
holler.active.your-projectSession storagePeople who opened the feedback link, to keep the button on in that tab.
holler.reviewerLocal storagePeople who opened the feedback link. A random ID that groups their notes.
holler.nameLocal storageReviewers who type their name, so they don’t have to again.

It also sets window.__hollerLoaded so it doesn’t start twice if the tag is added twice.

What it never does

  • Read the bodies or headers of your site’s requests and responses.
  • Read cookies. document.cookie doesn’t appear in the code.
  • Record keystrokes. The only key it listens for is Escape, while the reviewer is pointing at something. Typed text comes only from the note box.
  • Store an ID for, or record anything about, visitors who didn’t open your feedback link.
  • Load other scripts, use eval, or send data to any service other than the Holler site it came from.

Worth knowing

  • Live captions use the browser’s own speech recognition. Some browsers run it on their maker’s servers; Chrome, for example, sends the audio to Google. The recording sent to Holler is transcribed, then discarded (details).
  • Error messages are kept as written. If your app logs personal data with console.error, notes can include it.
  • The bundled screenshot library includes code for web workers. It’s never started: the script doesn’t give it a worker file.

Size and version

  • Version 1, built 2026-10-14
  • 52.7 KB, 19.1 KB compressed
  • SHA-384: +mDG4EPhMdhfGScU9saqfR3zCNWufXcBZeAMOU+fA5TjH43oyGLQX73wqg0P3tI2

Check it yourself

Or ask your coding agent to compare the code with this page before you add the tag:

Review Holler's Feedback button script before I add it to this app. Don't change any code.

1. Fetch /w/v1.readable.js. It is the readable build of the Feedback button script, /w/v1.js.
2. Read /widget, which says what the script does, what it records and sends, and where.
3. Check the code against each claim on that page. For each claim, say whether the code matches it.
4. List anything else the code does that the page doesn't mention: network requests, storage, browser APIs it patches or listens to, elements it adds to the page, permissions it asks for.

Then report back.

Holler’s source repository is private. The readable file comes from the same source in the same build as the minified one, but it can’t prove that byte for byte. If you need that, review the minified file and pin it.

Pin a version

This tag loads one exact file. With integrity set, the browser refuses to run it if a single byte differs. Pinned files never change and stay available.

<script src="/w/v1-b87801a732.js" integrity="sha384-+mDG4EPhMdhfGScU9saqfR3zCNWufXcBZeAMOU+fA5TjH43oyGLQX73wqg0P3tI2" crossorigin="anonymous" data-project="your-project" defer></script>

The trade-off: a pinned version doesn’t get fixes. Check this page for a newer version and update the tag when you choose.

If your site has a Content Security Policy

Add these to the matching directives:

script-src 
connect-src 
img-src data:
style-src 'unsafe-inline'
  • script-src loads the script; connect-src sends notes. For screenshots to include images and fonts from other domains, such as a CDN, connect-src must allow those too; otherwise they appear blank.
  • img-src data:: screenshots are drawn through a data: image.
  • style-src 'unsafe-inline': the button’s styles are a <style> inside its Shadow DOM, and screenshots apply inline styles. Without it the button is unstyled and screenshots fail.
  • Voice notes need the microphone. If your Permissions-Policy blocks it, reviewers can still point and type.

Removing it

Delete the tag. Nothing else is needed. The storage keys above stay in visitors’ browsers until they clear site data, with no script left to read them. Notes already sent stay in your project until you delete it.

If the code does anything this page doesn’t list, that’s a bug in the page or the script. To report it, use the contact address that will be listed here before sign-in opens to everyone (for the Feedback button).