Scarcity Tell
A countdown timer that resets isn't urgent. It's lying, and your browser remembers what it said last time.
Scarcity Tell
A countdown timer that resets isn't urgent. It's lying, and your browser remembers what it said last time.
Date: 2026-10-19
Form factor: Browser extension (Manifest V3)
Browser surface: content script, toolbar popup, chrome.storage.local
Status: Prototype
What it is
Scarcity Tell reads Shopify product pages for the three claims that show up again and again in dark-pattern research: a countdown timer, a "only N left" stock line, and a "people are viewing this" social-proof number. It saves what each one said and compares it against what the same claim said the last time you were on that page. A timer that should have ticked down but shows more time than before, or a stock count that hasn't moved in three days despite "almost sold out" — that's what gets flagged, on the page, with the old and new numbers side by side.
Why it has to be an extension
A site has no reason to tell you its countdown reset, and it has every reason to hope you didn't notice. The only way to catch it is to keep a memory the site doesn't control: a local record of what the page claimed last time, tied to your browser rather than to any account on the retailer's side. That's chrome.storage.local, read and written by a content script on every visit, which is not something a page you're looking at can do to itself.
Who it serves
Online shoppers who've started to suspect the "4 left in stock" banner is decorative. Fake urgency is specifically named in the EU's planned Digital Fairness Act, Australia's ACCC fined Emma Sleep roughly $15 million AUD this year for a countdown timer that reset before each sale actually ended, and a Princeton crawl of 11,000 shopping sites found countdown timers on 361 of them and low-stock messages on 581 — this isn't a rare trick, it's a standard fixture people have stopped trusting but can't easily prove wrong without a tool that remembers what they saw.
Why it could be profitable
Free: unlimited scanning, on-page flags, and a running history capped at 20 tracked claims. Pro ($3/month or a $20 one-time unlock): no cap on tracked claims, plus an exportable evidence log — timestamped before/after readings a shopper could attach to a chargeback dispute or a complaint to a consumer-protection body. That export and any cross-device sync would need a real backend, which this prototype doesn't build; it's noted as roadmap, not shipped. The harder question is whether "prove the timer lied" is worth paying for versus just not buying from that store again — I think the chargeback-evidence angle is the stronger of the two, but it's unproven outside this one prototype. A secondary, more speculative lane: licensing the anonymized, aggregated flag data to researchers or regulators tracking how common these patterns are at scale, which is close to what the Princeton study already does manually.
How to load it in Chrome
- Open
chrome://extensionsand turn on Developer mode. - Click Load unpacked and select this folder's
extension/directory. - Visit any product page on a
myshopify.comstorefront. Scarcity Tell reads the page once it settles, and flags appear inline the first time it sees a claim repeat with a contradiction — which means the first visit to any given page just records a baseline.
How to try the demo
- Open
index.htmlin any modern browser — it works straight fromfile://, no local server needed. - Click the Scarcity Tell button in the mock toolbar to open the popup over the mock product page.
The mock page simulates a second visit to a running-shoe listing: the countdown now shows more time remaining than it did an hour ago, the stock line still says "2 left" after three days, and the viewer count has swung from 4 to 51 between loads. The popup and the on-page flags both reflect that comparison. On the real extension, content.js does this scan and the storage comparison live, on whatever page you're on; here, script.js recreates that one comparison from sample-data.json since there's no live page to read.
Permissions, and why each one
| Permission | Why it's needed |
|---|---|
storage |
Saves what each timer, stock line, and viewer count said last time, so there's something to compare against on your next visit. |
activeTab |
Lets the popup identify the tab you're on and ask its content script what it found there. |
What's in this prototype
- A content script that scans Shopify storefront pages for countdown timers, low-stock claims, and viewer-count claims, using text-pattern matching rather than per-theme selectors.
- Per-claim history in
chrome.storage.local, keyed by page and claim type, with flags raised on timer resets, static stock counts, and volatile viewer claims. - Inline on-page badges on the flagged elements, plus a popup showing the current page's flags and a scrollable log of everything caught recently across other pages.
- A free-tier cap at 20 tracked claims, with an upgrade note in the popup.
- A demo harness pre-seeded with 20 tracked claims and a 13-entry flag history across eight mock storefronts, so the popup has a real log to show beyond the one live page.
Roadmap
- Match custom Shopify domains (not just
myshopify.com) by detecting the storefront's ownShopify.shopglobal instead of relying on the URL. - Add WooCommerce and BigCommerce pattern sets once the Shopify version has real usage to learn from.
- Build the evidence-export and sync backend the paid tier depends on.
- Let a flagged claim link straight to the FTC or ACCC complaint form pre-filled with the before/after reading.
- Track price alongside the countdown, since a few sites pair a resetting timer with a price that quietly ticks back up once it "ends."
Sources
- EU Digital Fairness Act Targets Dark Patterns — the European Commission's planned 2026 legislation names fake countdown timers and "only 2 left in stock" claims as examples of the dark patterns it would cover.
- Emma Sleep fined $15m for fake countdown timers and phantom discounts — Australia's ACCC action over a timer that reset itself before each sale period actually ended.
- Dark Patterns at Scale: Findings from a Crawl of 11K Shopping Websites — the Princeton study behind the 361-site countdown-timer and 581-site low-stock-message figures.
Requirements
Scarcity Tell — Requirements
Goals
- Detect the three most common fake-urgency claims on Shopify product pages: countdown timers, low-stock lines, and viewer-count social proof.
- Remember what each claim said on a prior visit and surface a contradiction (reset timer, static stock count, inconsistent viewer count) the moment it's visible.
- Keep every comparison local to the user's browser — no account, no server, no data leaving the device in this prototype.
- Make the free tier genuinely useful (unlimited scanning) while giving a paid tier a real reason to exist (unlimited history plus export).
Primary user
A shopper who buys from independent online stores regularly enough to have noticed the same "4 left in stock" banner on three separate visits. They don't need a lecture on dark patterns — they want the specific contradiction, with the old and new numbers, so they can decide whether to trust the page or close the tab.
Functional requirements
- FR1: On a matching page, the content script scans for countdown-timer elements using a time-pattern regex combined with nearby urgency keywords.
- FR2: The content script scans for low-stock claims matching patterns like "only N left," "N left in stock," and "almost sold out."
- FR3: The content script scans for viewer-count claims matching patterns like "N people are viewing this."
- FR4: Each detected claim gets a stable key derived from its category and the static (non-numeric) text around it, so the same claim can be matched across visits even if the number changes.
- FR5: For each claim, the extension loads any prior reading for that key from
chrome.storage.localbefore deciding whether to flag it. - FR6: A countdown timer is flagged when its computed deadline is later than the previously recorded deadline by more than a two-minute tolerance.
- FR7: A stock-count claim is flagged when the number is unchanged across readings more than one hour apart.
- FR8: A viewer-count claim is flagged when at least two prior readings exist and the values vary by more than 40% without a plausible explanation.
- FR9: Flagged elements get an inline badge and a title attribute showing the previous and current values.
- FR10: Every new reading is written back to storage, with history per claim key capped at 5 entries.
- FR11: The total number of distinct claim keys tracked is capped at 20 on the free tier; once at the cap, new claims are scanned and flagged for the current visit but not added to persistent history.
- FR12: The popup shows the current page's flags and a scrollable list of the most recently flagged claims across all pages.
- FR13: The popup falls back to
sample-data.jsonwhenchrome.*APIs are unavailable, rendering the same markup the real popup would. - FR14: The demo harness reproduces one scan result (a resetting timer, a static stock count, and a volatile viewer count) without running the detection regex against real page text.
User stories
- As a shopper, I want to see when a countdown timer has more time left than it did on my last visit, so I know it isn't a real deadline.
- As a shopper, I want to see when a "low stock" number hasn't moved in days, so I can judge whether it reflects real inventory.
- As a shopper, I want to see when a "people are viewing this" number swings wildly between visits, so I can treat it as noise rather than demand.
- As a shopper, I want my scan history to stay on my device, so I'm not creating an account just to catch a fake timer.
- As a frequent user, I want a paid tier that removes the 20-claim cap and lets me export my evidence, so I can use it for a dispute without losing older flags.
Extension surfaces
- Content script — runs automatically on
myshopify.comproduct pages, scans the DOM, compares against stored history, and injects inline flags. - Toolbar popup — shows the current page's flags and a recent-activity log, and messages the content script for live results.
chrome.storage.local— the only persistence layer; holds per-claim history and the running flag log.
Non-functional requirements
- The content script's scan runs once per page load at
document_idleand does not poll or re-scan on a timer, to avoid interfering with the host page's own scripts. - Permissions are limited to
storageandactiveTab; no host permission beyond the declaredmyshopify.commatch is requested. - No data leaves the browser in this prototype; the export feature described in the README is explicitly unbuilt.
- The content script scopes its CSS classes with a
scarcitytell-prefix and guards against double injection.
Out of scope (for the prototype)
- Matching custom-domain Shopify stores (anything not on
myshopify.com). - WooCommerce, BigCommerce, or marketplace platforms like Amazon or Etsy.
- The evidence-export, account system, and cross-device sync described as the paid tier.
- Detecting dark patterns outside the three claim types listed above (e.g., drip pricing, forced continuity).
Open questions
- Is a two-minute tolerance on timer-reset detection tight enough to catch real resets without flagging ordinary page-load jitter? This prototype picked two minutes as a reasonable guess, not a tested threshold.
- Does the one-hour window for stock-count staleness produce too many false flags on stores that genuinely do carry low inventory for a day or two?
- Would shoppers value an exportable evidence log enough to pay for it, or is the flag itself the whole value and export is dead weight?