Skip to content
academia.sh

Lesson 20 / 24

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.

Contents

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

// 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?

To keep your progress and take notes, Log in

My notes

Log in to take notes.

Start typing to search.

↑↓ Esc navigate · open · close