Visual bug reporting: a widget lets someone draw on the page, and the report arrives with a screenshot, the console log and the browser details attached.
Build me visual bug reporting that replaces Userback: somebody draws on the page, and the report arrives with the screenshot, the console and the browser details. STACK - Node 20+ with Fastify - SQLite through better-sqlite3, WAL mode - One embeddable widget under 40KB gzipped, from my own domain - html2canvas or the modern equivalent for the capture, plus rrweb if you want the last few seconds of session - Caddy in front THE DATA MODEL - projects: id, name, allowed_origins_json, widget_secret, default_assignee, is_active - reports: id, project_id, kind, title, body, priority, status, page_url, page_title, reporter_name, reporter_email_hash, created_at, resolved_at — kind is bug, idea, praise, or a survey answer - screenshots: id, report_id, kind, path, sha256, width, height, bytes — the raw capture and the annotated one, kept separately - annotations: id, report_id, kind, geometry_json, colour, text, position — stored as objects rather than only burned into the image, so they can be re-rendered or edited - environment: report_id, browser, browser_version, os, device, screen_json, viewport_json, pixel_ratio, language, timezone, url, referrer, user_agent_bucket - console_events: id, report_id, level, message, stack, at_ms - network_events: id, report_id, method, url_path, status, duration_ms, at_ms - session_blob: report_id, path, bytes — the optional replay of the last N seconds - user_traits: report_id, external_id, traits_json — whatever my application passed in - integrations, deliveries: pushing the report into an issue tracker THE CAPTURE, WHICH IS THE HARD PART - Capturing the page from inside the browser is not a real screenshot: it is a re-render of the DOM. Cross-origin images, iframes, canvas elements, shadow DOM and complex CSS all render imperfectly or not at all - Say so honestly in the interface: what the reporter sees in the preview is what will be sent, so if it looks wrong they can say so - The alternative is the browser's own display capture, which produces a true image but requires a permission prompt and shows a picker. Offer it as the better option where available, and fall back to the DOM render - Capture the scroll position and the full page as well as the viewport, because a bug is often above or below what is visible - Redact by selector before capture, and by default mask every password and payment field. Somebody reporting a bug from a page with their own data on it is handing you that data ANNOTATION - Draw a rectangle, an arrow, freehand, or a highlight; add a text note; blur a region - Blur must destroy the pixels in the sent image, not cover them with a shape that can be peeled off - Annotations kept as objects and also burned into a flattened copy, so the report is readable anywhere and still editable - Works with a finger on a phone, which is where a good share of bug reports come from WHAT IS COLLECTED AUTOMATICALLY - Browser, version, operating system, device, screen and viewport size, pixel ratio, language, time zone — the details nobody ever includes when asked - Console output from the last N seconds, with errors and their stacks. This is the single most useful field in the whole report and it is the one reporters never think to attach - Failed network requests with their status and timing - The page URL, the referrer, and any traits my application passed in — plan, account, role - Optionally the last few seconds of session replay, with the same masking rules as any replay tool, and off by default because it is far more invasive than a screenshot PRIVACY - Everything above is captured from a real person's screen. Mask by default, allow-list what is revealed, and never capture a password field under any configuration - The widget sends only to my own domain - A stated retention with a sweeper that deletes, and deletion by reporter on request THE WIDGET - A small launcher, or triggered from my own code - Shadow DOM so my page's styles and its styles cannot reach each other - Keyboard operable, focus trapped while open, Escape closes, focus returned - The whole flow in three steps: capture, annotate, describe. Every extra step loses reports, and a bug nobody reports is a bug you find from a customer leaving - Works on a phone, in a modal that does not cover what is being reported ROUTING - Push into an issue tracker with the screenshot attached and the environment in the body, formatted so it is readable in that tracker - Two-way where the tracker allows: closing the issue notifies the reporter, which is what turns bug reporting into something people do twice - Email and webhook as alternatives, signed and retried - Every delivery recorded THE INBOX - Reports listed with a thumbnail, filterable by project, status, priority, page and browser - Grouping by page and by browser, which is how a pattern becomes visible - Assign, comment, resolve, with the reporter notified OPERATIONS - .env: DATABASE_PATH, STORAGE_PATH, BASE_URL, WIDGET_SECRET, SESSION_SECRET, HASH_SALT, RETENTION_DAYS - Migrations on boot, each once - Nightly backup off the machine, restore script - Health endpoint WHAT MATTERS MOST The console capture and the masking. Attach the last errors automatically — that is what turns a vague report into a fixable one — and mask sensitive fields before the image is ever created. Then test the capture on your ugliest page, because DOM rendering will surprise you. Give me the repository, the widget, migrations, .env.example, one tracker integration, and a README with deploy steps behind Caddy and the capture limitations listed.
What you lose
- Screenshot capture from inside the browser, including the annotation layer
- Console errors and network failures captured automatically with the report
- Routing into issue trackers already built
If you would rather not build
- Sentry user feedback, if you already use it
- A form with a file upload, which is most of the value
What it costs
read from their page 15 Aug 2026
| Plan | Billed monthly | Billed yearly | Last read |
|---|---|---|---|
| — | $39/mo | — | 15 Aug 2026 |
Their pricing page is where these came from. Seeing a different price? Tell us.
The escape hatch
open source · no votes, no paid placement
html2canvas
$0Renders the current DOM to a canvas for a screenshot with no prompt.
niklasvh/html2canvasfree · open source
Formbricks
$0Self-hosted forms for the report itself, with file attachments.
formbricks/formbricksfree · open source
Why this verdict
our own opinion · changed only by a person
80/100
Verdict yes at 80. The console buffer is the feature worth copying: it turns a useless report into a fixable one, and it is fifteen lines.
History
tracked since 10 Aug 2026 · nothing is ever overwritten
Questions about Userback
answered from the record above
Is Userback free?
No — the plan we track is $39 a month. Startup at $39/month billed monthly, $29 annually, for five seats; Company is $99 monthly.
Can you replace Userback by building your own?
YES. Replaceable in one session with an AI coding agent. Replacement score 80 out of 100, build time one session. Read what you lose before you decide.
How much does Userback cost?
$39 a month on Startup — $468 a year. Recorded 14 Aug 2026.
What do you lose by replacing Userback?
Screenshot capture from inside the browser, including the annotation layer; Console errors and network failures captured automatically with the report; Routing into issue trackers already built. If any of those carry weight for you, keep paying.
Is there an open-source alternative to Userback?
Yes: html2canvas, Formbricks. The prompt on this page is for when you want it your way instead.
Related entries
same category first, most replaced first
Every week, something stops being worth paying for.
New verdicts, prices that moved, entries added. One email a week. Unsubscribe in one click. Nothing is being sent yet — your address is kept here, and the first issue is the first thing it is used for.
free forever · no tracking pixel · stored here, never passed to anyone

