DATASCI 210 Capstone  ·  Spring 2026

Detecting Dark Patterns
on the Open Web

A Chrome extension that runs a two-stage ML pipeline: semantic embeddings computed in-browser and eight XGBoost classifiers on the server, identifying deceptive UI patterns in real time as you browse.

8 Pattern Classes
80+ ML Features
~80% Elements Filtered
<200ms End-to-End
Official Trailer

They hide in plain sight.
Not anymore.

Two minutes. Eight pattern types. One browser extension that catches what you miss.

Watch the promo on YouTube
2 min  ·  YouTube

Dark Patterns Are Everywhere

Deceptive design patterns, commonly called dark patterns, are manipulative UI techniques deployed by websites to influence users into actions that benefit businesses at users' expense: guilt-laden decline buttons, fabricated stock limits, deliberately confusing opt-out language, and interfaces that bury privacy controls.

Unlike outright fraud, dark patterns operate in a legal gray zone: psychologically coercive but technically compliant. A 2019 study by Mathur et al. found dark patterns on over 11% of 11,000 shopping websites surveyed. Regulatory attention is growing as the FTC and European regulators issue guidance and enforcement actions, yet users remain largely unaware while browsing.

The core challenge is detection at scale. Dark patterns depend on contextual signals including visual asymmetry, linguistic framing, and interface hierarchy that keyword rules alone cannot capture. Existing academic datasets label patterns at the element level but generalize poorly to live, adversarially designed production pages.

11% of shopping sites use dark patterns (Mathur et al. 2019)
FTC and EU regulators issuing enforcement actions
Zero real-time detection tools available to everyday users, until now
ShopNest cart showing 9:46 countdown timer pressuring immediate checkout
Urgency: A manufactured countdown timer appears in both the banner and order summary, pressuring immediate checkout. This timer is cosmetic.

Real-Time Detection,
Right in Your Browser

The Deceptive Pattern Detector is a Chrome extension that automatically scans pages as you browse with no manual action required. It flags manipulative UI elements with plain-English explanations and calibrated confidence scores, giving users the awareness to make truly informed decisions.

Who It's For

Any user browsing e-commerce sites, SaaS sign-up flows, subscription services, or hotel and travel booking pages, where dark patterns are most prevalent. No technical knowledge required.

Privacy First

Semantic embeddings are computed entirely in your browser via ONNX Runtime WebAssembly. Only stripped, anonymized candidate elements above a confidence threshold ever reach the classification server.

Low Latency

The two-stage gatekeeper filters roughly 80% of elements before any server call. In-browser embedding completes in under 150 ms. Full end-to-end classification under 200 ms.

Plain-English Alerts

Rather than raw model scores, the popup explains what was detected and why it is manipulative, raising awareness rather than silently blocking content.

Sol Studio product page with ONLY 4 PAIRS LEFT IN YOUR SIZE scarcity claim
Scarcity: A fabricated low-stock claim alongside a "Limited Drop" badge combines urgency language with visual styling to pressure immediate purchase.
Hargreave hotel booking with pre-checked Travel Protection charge of 49.98 euros
Forced Add-on: A pre-checked travel insurance fee silently adds €49.98 to the total. The charge appears in the order summary before the user notices the checkbox.

A Two-Stage ML Pipeline

The system resolves a core tension: accuracy requires semantic understanding, but sending every UI element to a server is too slow and raises privacy concerns. The architecture runs expensive work in-browser first, then sends only high-confidence candidates through the network.

Stage 1: In-Browser JavaScript · ONNX Runtime WASM · No network calls
1
choice_set_extractor.js

Choice Set Extraction

Walks the Accessibility Tree and DOM to identify decision-point UI groups: clusters of buttons, links, and checkboxes that together present a binary or multi-way choice. Each set captures option labels, CSS computed properties, and subtree structure.

2
noise_classifier.js

Noise Filtering

Uses Jaccard similarity and keyword matching to discard navigation chrome, footers, social share buttons, and other non-decision UI. Eliminates the majority of candidate elements at negligible compute cost.

3
offscreen_embedder.js

In-Browser MiniLM

Runs all-MiniLM-L6-v2 via ONNX Runtime WebAssembly in a dedicated offscreen document, generating 384-dimensional semantic vectors for choice-set text without any network call. Embedding completes in under 150 ms.

4
dp_gatekeeper.js

Gatekeeper

Applies cosine-similarity threshold against dark-pattern anchor embeddings. Only candidates whose similarity exceeds the gate (roughly 20% of inputs) proceed to the server, cutting both latency and data exposure.

Stage 2: Server Flask API · XGBoost · scikit-learn calibration
5
Flask API · XGBoost

Classification + Calibration

Eight independent binary classifiers evaluate 80+ engineered features: text signals, CSS asymmetry, structural properties, and embedding features. Raw probabilities are post-hoc calibrated via Platt scaling for meaningful confidence scores.

6
Extension Popup

User Alert

The popup displays flagged patterns with plain-English descriptions and calibrated confidence scores. The toolbar badge icon updates to reflect the current page risk level at a glance.

Text Signals
  • Urgency phrases and countdown timers
  • Scarcity claims ("only N left")
  • Social proof language
  • Guilt-framing in decline buttons
  • Promotional and discount language
CSS Aggregates
  • Font size and weight per option
  • Opacity and visibility flags
  • Low-contrast and gray-text counts
  • z-index and position:absolute outliers
  • Button sizing differentials
Structural
  • AX subtree size
  • Option count and label lengths
  • Dialog and modal detection
  • Duplicate container count
  • Nesting depth
Embedding
  • MiniLM cosine similarity
  • Accept vs. decline role scores
  • Choice asymmetry signals
  • Cross-option semantic distance

Eight Classes We Detect

The classifier targets eight classes drawn from established academic taxonomies (Gray et al. 2018; Mathur et al. 2019) and refined for detectability from client-side structure. Select any card to expand.

Building the Training Pipeline

Each pattern class is detected by a dedicated binary XGBoost classifier trained on real labeled captures augmented with calibrated synthetic data. The pipeline covers collection, labeling, augmentation, model fitting, and calibration.

01

Real Captures

A Playwright-based browser automation pipeline navigated live websites, extracted choice sets from the Accessibility Tree and DOM, and stored results including option text, CSS properties, and subtree structure in a PostgreSQL database.

02

Human Labeling

Captured choice sets were reviewed by human annotators using a custom labeling toolchain, assigning each a decision (dark or not dark) and, for dark examples, one or more pattern-type tags from the eight-class taxonomy.

03

Synthetic Augmentation

Rare patterns required synthetic augmentation: pattern-specific choice sets generated programmatically with distributions calibrated to match real captures (dialog rates, option counts, CSS targets, text-regex hit rates) to reduce distributional shift.

04

Per-Pattern Classifiers

One XGBoost binary:logistic model per pattern. Class imbalance handled via scale_pos_weight. Early stopping with grouped shuffle splitting prevents within-site leakage. Hyperparameters: max depth 5, lr 0.05, subsampling 0.9.

05

Platt Scaling Calibration

Raw XGBoost probabilities are post-hoc calibrated by fitting a logistic regression on held-out logit scores. Validated for reduced Brier score and log-loss across all eight classes on a golden real-labeled set.

06

Choice Set Abstraction

Rather than evaluating elements in isolation, the system groups related elements into choice sets. An accept button and its guilt-laden decline link form one set, letting the model reason about the relationship between options.

Built for Iteration

Two custom dashboards drove the entire model lifecycle: one for training and evaluating classifiers, one for monitoring how those models perform against real-world extension traffic.

Model Playground interface
Model Training

Model Playground

A browser-based training environment built into the Flask backend. Researchers select one of the eight pattern classes, tune XGBoost hyperparameters, toggle individual features from the 84-feature catalog, and launch a full cross-validated training run: without touching a terminal.

  • Per-pattern training: each of the eight classifiers is trained independently, with positive counts and full run history displayed inline.
  • Synthetic + golden fusion: calibrated synthetic negatives are injected before splitting; the run is then evaluated against the curated golden set to measure real-world precision.
  • Live ROC & PR curves: rendered after each run with Youden's J and best-F1 operating points annotated directly on the chart.
  • Feature importance: XGBoost gain scores for the top-k features reveal which signal families (cosine similarity, lexical, visual, structural) drive each classifier.
  • Sigmoid calibration: a post-hoc Platt calibrator is fit on a held-out split; Brier score before and after is shown to confirm outputs are true probabilities.
  • One-click promote: qualifying runs are promoted to production from the UI; the served model refreshes within minutes via a TTL cache.
Gatekeeper Analysis Dashboard
Traffic Monitoring

Gatekeeper Analysis Dashboard

A server-side analytics dashboard that turns the raw stream of extension events into structured insight: making it possible to measure how effectively the two-stage pipeline filters noise before any classifier runs.

  • End-to-end funnel: tracks every page scan from initial extraction through noise filtering, cosine-similarity gating, server receipt, and final pattern confirmation in a single stat row.
  • Pass vs. Block over time: a daily time-series chart shows how gate pass rates shift as browsing patterns and threshold settings change.
  • Score distribution: overlapping histograms of cosine similarity for gate-filtered, dark-pattern-positive, and gate-passed-no-result candidates reveal where the threshold sits relative to the score mass.
  • Per-domain breakdown: a ranked table of domains by pass count, block count, and confirmed server hits identifies which sites produce the most real patterns.
  • Calibrated confidence: per-class probability scores from the promoted classifiers are shown alongside raw gate scores, validating that calibration holds on live traffic.

What We Achieved

8 Pattern classifiers, one per type
80+ Engineered features per choice set
~80% Elements filtered before any server call
<150ms In-browser MiniLM embedding time
<200ms End-to-end classification latency
Technical Achievement

In-Browser First Stage

Running a 23 MB MiniLM model via ONNX Runtime WASM in a Chrome MV3 extension without blocking the main thread required a dedicated offscreen document and message-passing architecture. Embedding completes in under 150 ms on representative hardware.

Technical Achievement

Gatekeeper Efficiency

The dual noise-filter and cosine-similarity gate reduces server calls by roughly 80% versus a naive all-elements approach. The extension runs unobtrusively on content-rich pages without perceptible latency or privacy leakage from user browsing context.

Technical Achievement

Production Deployment

The classification API is deployed at darkpatterns.duckdns.org behind HTTPS, handling concurrent requests from multiple active extension users with average response latency under 200 ms for a full scoring request.

Key Learning

Choice Sets Beat Elements

Evaluating elements individually misses most dark patterns. The manipulation is expressed in the relationship between options: a prominent accept versus a hidden decline, not in any single element alone. Choice set abstraction was the critical architectural decision.

Key Learning

Synthetic Data Fills Gaps

Rare patterns like privacy zuckering required programmatic augmentation. Calibrating synthetic distributions to match real-capture statistics (dialog rates, CSS targets, option counts) was essential to avoiding distributional shift between training and live inference.

Future Work

Long-Term Potential

Expanding training captures from SaaS and checkout flows, refining synthetic distributions for rare patterns, cross-browser compatibility (Firefox, Edge), and evaluating lightweight server-side model alternatives for better scalability.

Built at Berkeley

Five UC Berkeley MIDS students in the Spring 2026 cohort built this project end-to-end: from dataset curation and model training to the Chrome extension and Flask backend.

JL
Jane Lai
ML Engineering and Model Training

Focused on XGBoost model architecture, feature engineering, and calibration pipeline design across all eight pattern classifiers.

iSchool Profile →
BP
Bianka Paul
Full Stack and Extension Development

Led Chrome extension architecture, popup UI, and integration with the backend evaluation API and gatekeeper layer.

iSchool Profile →
CR
Chris Rezny
Backend and Data Pipeline

Built the Flask API, PostgreSQL schema, Playwright capture pipeline, and the custom labeling toolchain for choice-set annotation.

EG
Eduardo Gonzalez
NLP and Feature Engineering

Designed the V3 text signal feature catalog spanning urgency, scarcity, social proof, guilt-framing, and promotional phrase detection.

iSchool Profile →
SA
Saleh Alhumaid
Data Science and Model Evaluation

Led dataset labeling strategy, synthetic data generation pipelines, calibration evaluation, and model performance benchmarking.

iSchool Profile →