Everything the extension does, and what ends up in a shared review or an exported file.
Website Feedback Tool is a Chrome extension for visual feedback. You click on a page to leave comment pins, then share a link. Whoever opens the link sees a frozen copy of the page with your pins on it — no extension, no account, no setup on their side.
The extension only ever activates on the tab you point it at, and only when you ask it to.
In annotation mode, hovering highlights the element under the cursor. Click it, type your note, press Enter. A numbered pin appears and stays anchored to that element — it follows the element when the page scrolls, resizes, or reflows.
Press and drag to draw a rectangle instead of a single point. Useful when the problem is a region — a broken layout block, a group of items — rather than one element. Drags under 10 px count as clicks, so a shaky hand won't create stray boxes.
Alt + A pauses annotation without discarding anything — handy when you need to click a link, open a menu, or scroll a widget that annotation mode would otherwise intercept. Press it again to resume.
Save & Share captures a snapshot of the page — markup, styles, images, canvas and video frames as they are at that moment — uploads it, copies the link to your clipboard and opens it in a new tab. Your original page is left clean: pins are cleared and annotation mode switches off.
Whoever opens the link can:
The clock icon in the popup header opens Your reviews — everything you have shared, newest first, with the page title, comment count and age. Click one to reopen it.
Hover an entry and click ✕, then confirm. The snapshot is deleted from the server immediately and the link stops working for everyone.
Deletion works through a secret token stored in your browser when the review was created — that's what proves the review is yours, since there are no accounts. Consequences worth knowing:
Export comments as JSON in the popup downloads the comments as a file — available both while annotating and while viewing a shared review.
The file is built to be acted on, not just read. Every comment carries the CSS selector of the element it's about, what that element is, its computed styles, the CSS rules that match it, and its ancestor chain — enough to open devtools and go straight to the fix. Page and viewport dimensions travel along, because a coordinate on its own means nothing on someone else's screen.
Typical uses: attaching to a bug ticket, feeding an AI assistant, or scripting a report.
Files are named wft-comments-<host>-<date>.json. Exporting from a
review takes the styles as they were when the snapshot was captured, not as the live site
looks today.
Current version: formatVersion: 1. Check this field before parsing — it is
bumped whenever the shape changes incompatibly.
| Field | Type | Meaning |
|---|---|---|
tool | string | Always "Website Feedback Tool" — lets a consumer recognise the file. |
formatVersion | number | Schema version. |
exportedAt | string | ISO-8601 UTC time the file was produced. |
page | object | The page the comments are about. |
environment | object | Browser and screen facts: ua, platform, language, screen, viewport, dpr, colorDepth. May be null. |
commentCount | number | Length of comments. |
comments | array | Comments in the order they are numbered on the page. |
page| Field | Type | Meaning |
|---|---|---|
url | string | Address of the annotated page — for an export made from a review, the original page, not the review link. |
title | string | Page title at capture time. |
capturedAt | string | ISO-8601. From a review, this is when the snapshot was taken, which may be long before exportedAt. |
layout.viewport | object | {width, height} of the visible area, CSS pixels. |
layout.document | object | {width, height} of the full scrollable page. |
layout.scroll | object | {x, y} scroll offset — relates page coordinates to viewport ones. |
layout.devicePixelRatio | number | 2 on a typical HiDPI screen. |
comments[]| Field | Type | Meaning |
|---|---|---|
number | number | 1-based — the number drawn on the pin. |
id | string | Internal id. Useful for de-duplication inside one file, nothing more. |
text | string | What the reviewer wrote. It's user input — escape it before rendering as HTML. |
type | string | "point" (a pin) or "rect" (a dragged region). |
position | object | {x, y} page coordinates of the pin; for a region, its top-left corner. |
size | object | {width, height} — regions only. |
target | object | What the comment is about. See below. |
comments[].targetResolved when the export runs, against the DOM in front of you. From a review page that DOM is the captured page, so the styles are the ones the reviewer saw.
| Field | Type | Meaning |
|---|---|---|
found | boolean | Whether the element was located. If false, the block carries reason, plus selector and lastKnownRect where known. |
reason | string | "not-in-dom" — the element is gone; "area-selection" — a region whose centre couldn't be hit-tested; "no-selector". |
lastKnownRect | object | Geometry recorded when the comment was made, for elements that have since disappeared. |
resolvedBy | string | "selector", or "area-center" when the element was found by hit-testing the centre of a region. |
selector | string | CSS selector for the element — paste it into devtools. |
element | object | tag, id, classes, attributes, text, truncated html. |
rect | object | page and viewport coordinates plus width/height. |
pointInElement | object | {x, y} as fractions 0..1 of the element's box — the one part of the geometry that doesn't depend on screen size. Absent for regions. |
styles | object | Computed values grouped as layout, box, typography, visual. |
matchedRules | array | The authored CSS rules matching the element: selector, css, optional media, and source. |
ancestors | array | Up to 3 parents with display, position, overflow, transform, zIndex, size. |
scrollContainer | object | Selector of the nearest scrollable ancestor, when the comment sits inside one. |
pinnedToViewport | boolean | Present when the element was fixed or sticky at the time the comment was made. |
truncated: true.{
"tool": "Website Feedback Tool",
"formatVersion": 1,
"exportedAt": "2026-08-20T09:20:00.000Z",
"page": {
"url": "https://example.com/pricing",
"title": "Pricing — Example",
"capturedAt": "2026-08-20T09:15:00.000Z",
"layout": {
"viewport": { "width": 1280, "height": 720 },
"document": { "width": 1280, "height": 4200 },
"scroll": { "x": 0, "y": 900 },
"devicePixelRatio": 1
}
},
"commentCount": 1,
"comments": [
{
"number": 1,
"id": "m9x2k1a",
"text": "The button doesn't respond on mobile",
"type": "point",
"position": { "x": 412, "y": 1180 },
"target": {
"found": true,
"resolvedBy": "selector",
"selector": "#pricing > div > button",
"element": {
"tag": "button",
"classes": ["cta"],
"attributes": { "data-testid": "buy", "aria-label": "Buy Pro" },
"text": "Buy now"
},
"rect": {
"page": { "x": 412, "y": 1180 },
"viewport": { "x": 412, "y": 280 },
"width": 132, "height": 44
},
"styles": {
"layout": { "display": "inline-block", "position": "static" },
"box": { "padding": "12px 20px", "boxSizing": "border-box" },
"visual": { "backgroundColor": "rgb(91, 91, 214)", "pointerEvents": "auto" }
},
"matchedRules": [
{
"selector": ".pricing-card .cta",
"css": "background: #5B5BD6; color: #fff; padding: 12px 20px;",
"source": "https://example.com/assets/app.css"
},
{
"selector": ".pricing-card .cta",
"media": "(max-width: 600px)",
"css": "padding: 8px 12px;",
"source": "https://example.com/assets/app.css"
}
],
"ancestors": [
{ "selector": "#pricing", "tag": "section",
"styles": { "display": "block", "overflow": "hidden" } }
],
"pointInElement": { "x": 0.5, "y": 0.41 }
}
}
]
}
| What | Limit |
|---|---|
| Shared review lifetime | 90 days, then permanently deleted |
| Locally stored comments | 30 days per page, cleaned up automatically |
| Review history in the popup | Last 100 shares |
| Snapshot size | 50 MB per review |
| Sharing rate | 5 snapshots per minute, 30 per hour |
| Total storage | 10 GB; above it new snapshots are refused until old ones expire |
| Accounts | None — nothing to sign up for |
What is stored and for how long is spelled out in the Privacy Policy.
Chrome doesn't allow extensions on its own pages (chrome://, the Web Store,
other extensions' pages). Try a regular site.
For file:// pages, turn on Allow access to file URLs in the
extension's settings, then reload the page. The popup links you straight there.
Sharing is limited to 5 snapshots a minute and 30 an hour, which keeps one script from filling the server. Your comments stay on the page — wait the stated time and press Try again.
The snapshot couldn't be uploaded. Your comments are not lost — Try again keeps them. If it persists, save the page via File → Save as MHTML and send that instead.
Snapshots freeze the DOM and strip scripts, so anything that only exists while JavaScript is running — a carousel mid-animation, a lazily loaded image below the fold — is captured as it was at that moment. Scroll the whole page before sharing to give lazy content a chance to load.
Pins anchor to elements, not coordinates. If the site itself changed since you commented, an
anchor may no longer exist; in a JSON export those comments come back as
found: false with the geometry last recorded for them.
Either it expired after 90 days, or it was deleted from the review history. There's no way to restore it — take a fresh snapshot.