---
title: 'Dark Patterns and Ethics'
source: 'https://academia.sh/en/courses/user-experience/dark-patterns-and-ethics'
course: 'User Experience and Behavior Design'
language: en
updated: '2026-08-19T05:19:57+00:00'
license: 'CC BY-SA 4.0'
---

# Dark Patterns and Ethics

Turning a design decision's legitimacy criterion into countable indicators, and recognizing and rejecting the patterns of urgency, hidden costs, the roach motel, the consent trap, and false scarcity.

Across the seven lessons of this topic, the same question came up seven times. In the
salience lesson: would the user pick the first record if they knew why the list was
ordered this way? In the defaults lesson: would they leave the setting on if they
noticed it came pre-checked? In the social proof lesson: would they trust the number if
they knew how it was produced? In the progress lesson: would they keep waiting if they
knew the bar was not tied to actual work?

The question was the same every time, and every time one of two answers came out. This
lesson turns the question into a criterion, and the criterion into auditable
indicators.

## The Criterion

**A design decision is legitimate if the user would make the same choice even knowing
how that decision was made. If the decision's power comes from the user's ignorance,
that decision is a dark pattern.**

The criterion has two properties. First, it does not by itself treat changing the
user's behavior as a flaw — every design decision changes behavior; sorting records by
relevance changes it too. The flaw is the change being built on a false belief. Second,
it does not ask about the designer's intent. A pattern may have been built by accident;
the criterion still gives the same result, and the obligation to fix it is the same.

A **dark pattern** is an interface pattern that systematically violates this criterion,
turning the outcome toward what the designer wants by relying on the user's wrong
information. The five patterns below are this lesson's audit list.

**Urgency** is a pattern that narrows the user's thinking time without a real
constraint: countdowns that never end or that restart on every session.

**Hidden costs** is a pattern that discloses part of the price the user committed to
only after the commitment.

The **roach motel** is a pattern that makes entering a state easy and exiting it
disproportionately hard.

The **consent trap** is a pattern in which what the user believes they agreed to
diverges from what they actually agreed to: pre-checked boxes, layouts that make the
decline option invisible.

**False scarcity** is a claim of shortage about stock, time, or demand that is not
based on data.

None of these is given here as a recipe for implementation; each is defined as a
**recognition target**. The computation below ties recognition to measurement.

## Measuring the Indicators

```js
// audit.mjs — measuring an interface's dark-pattern indicators

// ---- 1) Effort asymmetry: the step gap between entering something and exiting it ----
const FLOWS = [
  { name: "borrow / return", entry: 2, exit: 2 },
  { name: "due date reminder", entry: 1, exit: 1 },
  { name: "recommendations newsletter", entry: 1, exit: 4 },
  { name: "membership", entry: 3, exit: 7 },
];
console.log("flow                          entry steps  exit steps  asymmetry  threshold (2x)");
for (const f of FLOWS) {
  const asymmetry = f.exit / f.entry;
  console.log(
    `${f.name.padEnd(29)} ${String(f.entry).padStart(11)} ${String(f.exit).padStart(12)}` +
    ` ${(asymmetry.toFixed(2) + "x").padStart(9)}  ${asymmetry > 2 ? "EXCEEDED" : "passed"}`
  );
}

// ---- 2) Hidden cost: the gap between the first disclosed commitment and the final total ----
const COSTS = [
  { name: "borrowing (free)", disclosed: 0, final: 0 },
  { name: "late return fee", disclosed: 0, final: 12 },
  { name: "inter-branch transfer", disclosed: 5, final: 5 },
  { name: "reservation + transfer + late fee", disclosed: 5, final: 23 },
];
console.log("\nitem                              disclosed upfront  final total  surprise ratio");
for (const c of COSTS) {
  const surprise = c.final === 0 ? 0 : (c.final - c.disclosed) / c.final;
  console.log(
    `${c.name.padEnd(34)} ${String(c.disclosed).padStart(14)} ${String(c.final).padStart(11)}` +
    ` ${((surprise * 100).toFixed(0) + " %").padStart(14)}  ${surprise > 0.2 ? "EXCEEDED" : "passed"}`
  );
}

// ---- 3) The scarcity claim's link to data ----
// For 12 records: actual shelf status and the "copies left" claim shown in the interface.
const ACTUAL = [1, 4, 2, 7, 1, 3, 9, 2, 5, 1, 6, 8];
const CLAIMS = {
  "derived from data": [1, 4, 2, 7, 1, 3, 9, 2, 5, 1, 6, 8],
  "fixed warning":     [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
  "partially tied":    [1, 3, 2, 3, 1, 3, 3, 2, 3, 1, 3, 3],
};
function correlation(a, b) {
  const n = a.length;
  const oa = a.reduce((x, y) => x + y, 0) / n, ob = b.reduce((x, y) => x + y, 0) / n;
  let num = 0, pa = 0, pb = 0;
  for (let i = 0; i < n; i++) { num += (a[i] - oa) * (b[i] - ob); pa += (a[i] - oa) ** 2; pb += (b[i] - ob) ** 2; }
  return pb === 0 ? 0 : num / Math.sqrt(pa * pb);
}
console.log("\nclaim form            correlation with actual stock  threshold (0.9)");
for (const [name, v] of Object.entries(CLAIMS)) {
  const r = correlation(ACTUAL, v);
  console.log(`${name.padEnd(21)} ${r.toFixed(3).padStart(30)}  ${r >= 0.9 ? "passed" : "EXCEEDED"}`);
}

// ---- 4) Whether the countdown is tied to a real deadline ----
// The same user's "time remaining" (minutes) seen across consecutive sessions.
const SESSIONS = {
  "real deadline": [58, 41, 27, 12, 3],
  "resets every session": [10, 10, 10, 10, 10],
  "resets on page refresh": [10, 10, 9, 10, 10],
};
console.log("\ncountdown                        sessions             decreasing");
for (const [name, v] of Object.entries(SESSIONS)) {
  let decreasing = true;
  for (let i = 1; i < v.length; i++) if (v[i] >= v[i - 1]) decreasing = false;
  console.log(`${name.padEnd(32)} ${v.join(", ").padEnd(20)} ${decreasing ? "yes" : "NO"}`);
}

// ---- 5) Five indicators together: auditing a design decision ----
// Criterion: would the user make the same choice if they knew how the decision was made?
// Indicators are measured individually; if even one is exceeded, the decision counts as a dark pattern.
const DECISIONS = [
  { name: "due date reminder opt-out default",         regret: 0.128, asymmetry: 1.0, surprise: 0.0, basis: 1.00, countdown: null },
  { name: "recommendations newsletter opt-out default", regret: 0.188, asymmetry: 4.0, surprise: 0.0, basis: 1.00, countdown: null },
  { name: "last copy warning (from stock)",             regret: 0.000, asymmetry: 1.0, surprise: 0.0, basis: 1.00, countdown: null },
  { name: "last copy warning (fixed)",                  regret: 0.000, asymmetry: 1.0, surprise: 0.0, basis: 0.00, countdown: null },
  { name: "reservation countdown",                      regret: 0.000, asymmetry: 1.0, surprise: 0.0, basis: 1.00, countdown: false },
  { name: "late fee at the last step",                  regret: 0.000, asymmetry: 1.0, surprise: 0.52, basis: 1.00, countdown: null },
];
const THRESHOLDS = { regret: 0.15, asymmetry: 2.0, surprise: 0.20, basis: 0.90 };
console.log("\ndecision                                     exceeded indicator              result");
for (const d of DECISIONS) {
  const exceeded = [];
  if (d.regret > THRESHOLDS.regret) exceeded.push("regret");
  if (d.asymmetry > THRESHOLDS.asymmetry) exceeded.push("effort asymmetry");
  if (d.surprise > THRESHOLDS.surprise) exceeded.push("hidden cost");
  if (d.basis < THRESHOLDS.basis) exceeded.push("unsubstantiated claim");
  if (d.countdown === false) exceeded.push("false urgency");
  console.log(
    `${d.name.padEnd(45)} ${(exceeded.length === 0 ? "-" : exceeded.join(", ")).padEnd(29)}` +
    ` ${exceeded.length === 0 ? "legitimate" : "DARK PATTERN"}`
  );
}
```

```
flow                          entry steps  exit steps  asymmetry  threshold (2x)
borrow / return                         2            2     1.00x  passed
due date reminder                       1            1     1.00x  passed
recommendations newsletter              1            4     4.00x  EXCEEDED
membership                              3            7     2.33x  EXCEEDED

item                              disclosed upfront  final total  surprise ratio
borrowing (free)                                0           0            0 %  passed
late return fee                                 0          12          100 %  EXCEEDED
inter-branch transfer                           5           5            0 %  passed
reservation + transfer + late fee               5          23           78 %  EXCEEDED

claim form            correlation with actual stock  threshold (0.9)
derived from data                              1.000  passed
fixed warning                                  0.000  EXCEEDED
partially tied                                 0.807  EXCEEDED

countdown                        sessions             decreasing
real deadline                    58, 41, 27, 12, 3    yes
resets every session             10, 10, 10, 10, 10   NO
resets on page refresh           10, 10, 9, 10, 10    NO

decision                                     exceeded indicator              result
due date reminder opt-out default             -                             legitimate
recommendations newsletter opt-out default    regret, effort asymmetry      DARK PATTERN
last copy warning (from stock)                -                             legitimate
last copy warning (fixed)                     unsubstantiated claim         DARK PATTERN
reservation countdown                         false urgency                 DARK PATTERN
late fee at the last step                     hidden cost                   DARK PATTERN
```

## Reading the Four Indicators

**Effort asymmetry** is the ratio of the number of steps required to enter a state to
the number required to exit it. Borrowing and returning are both two steps; the ratio
is 1.00. The recommendations newsletter turns on in one step and off in four; the ratio
is 4.00. This measurement is taken not from logs but from the interface itself: by
counting the two flows. Because counting requires no assumption, it is the indicator
audited with the least effort.

**Surprise ratio** is the share of the final total cost that was not disclosed at the
moment of commitment. The inter-branch transfer fee is disclosed from the start, so its
ratio is zero. The late fee is never disclosed, so its ratio is 100 percent. The fee
being conditional is not a justification: if the user does not know what happens in
case of a late return when they borrow, they have not seen part of their commitment.

**The scarcity claim's link to data** is the correlation between the displayed shortage
claim and the actual stock. The claim derived from data gives 1.000. A fixed warning
that writes "last copy" on every record gives 0.000 — the claim corresponds to nothing.
The third row measures an in-between case: the claim is partially related to stock
(0.807) but stays fixed once stock rises above three. A partially true claim does not
pass the audit; when the user looks at the number, they assume it is a measurement.

**Whether the countdown decreases** shows whether the urgency is tied to a real
deadline. With a real deadline, the remaining time seen across consecutive sessions
decreases. A countdown that starts from the same value every session does not decrease.
The third row shows a form that is easy to miss: the values wobble a little but do not trend
in one direction; the time is not actually running out, it is only being shown as if it
were.

## The Audit's Result and Its Limits

The last table applies five indicators to six decisions together. Two of the decisions
in the borrowing flow pass every threshold; four exceed at least one. Because the audit
also records which indicator was exceeded, it produces a direct correction list: the
newsletter's default is turned off and its cancel step is reduced to one; the fixed
warning is tied to stock data or removed; the countdown is tied to a real deadline or
removed; the late fee is written on the borrow confirmation screen.

The audit's limits must be stated clearly.

**The thresholds are a convention.** A factor of two for effort asymmetry, 20 percent
for surprise ratio, 0.90 for correlation — these are chosen values, not computed ones.
Their function is to turn debate into measurement: once a threshold is written down,
whether a decision exceeds it is not debated, it is measured. The threshold itself can
and should be debated.

**A decision that passes every threshold can still be flawed.** The audit catches
patterns that leave a measurable trace. A decline label that shames the user ("no
thanks, I do not want to save money") does not disturb any number; this form of the
consent trap can only be seen by reading the text. The audit is a screening tool, not
proof.

**No indicator can be offset by a rise in conversion.** A pattern raising the number of
borrows does not mean it passes the audit; as measured in The Power of Defaults lesson
and the Progress and Feedback lesson, the source of that rise is the user being
mistaken. The size of a number built on being mistaken cannot be a decision's
justification.

## Rejecting

This entire topic was meant to show how much power the designer holds over the user's
decision. The conclusion is that this power is already in use in every design
decision: there is no neutral default, no neutral ordering, no neutral emphasis.

This is why the professional stance cannot be "avoid influencing"; avoiding it is not
possible. The stance is this:

**What indicator a decision raises is written down.** When a change is proposed, what
it will increase must be stated openly. Is the rising number borrows, or is it consent
given unintentionally — the discussion cannot proceed without making this distinction.

**The criterion is applied on the user's behalf.** The user is not in the room; it is
the designer who applies the criterion in their place. If the answer to "would the user
make the same choice even knowing this" is no, the decision is rejected.

**A rejection is given with a reason.** The five indicators above turn the grounds for
rejection from a personal preference into an auditable finding. Instead of "I do not
want to do this," it is said "this decision exceeds the effort asymmetry threshold by a
factor of four, and the cancel step can be reduced to one."

**A legitimate alternative is proposed.** Every decision that fails the audit mostly
has a counterpart that serves the same purpose without violating the criterion: real
stock information instead of false scarcity, a symmetric flow instead of the roach
motel, the full cost disclosed at the moment of commitment instead of a hidden cost.

## Summary

- The legitimacy criterion is single: a decision is legitimate if the user would make
  the same choice even knowing how it was made; if the decision's power comes from
  ignorance, it is a dark pattern.
- Five patterns are defined as recognition targets: urgency, hidden costs, the roach
  motel, the consent trap, false scarcity.
- Four indicators leave a measurable trace: effort asymmetry, surprise ratio, the
  scarcity claim's correlation with data, and whether the countdown genuinely
  decreases.
- Because the audit records which indicator was exceeded, it produces a direct
  correction list; the thresholds are a convention, written down to turn debate into
  measurement.
- The audit is a screening tool: a decision that passes every threshold can still be
  flawed, and a rising conversion number cannot offset any indicator.
- The designer's stance is not to avoid influencing but to write down what number a
  decision raises, apply the criterion on the user's behalf, ground rejection in a
  reason, and propose a legitimate alternative.

## Next Step

Throughout this topic, applying the criterion continually required one thing: knowing
the user's informed preference, the real task duration, the useful-visit rate. None of
these is obtained by guessing. The next topic is devoted to measurement and begins with
the most basic question: how is it shown that an interface is good? How are task
success, duration, and error rate measured, how are these three numbers reduced to a
single metric, and where does the average become misleading?
