back

Project · on device · zero telemetry

Litmus

Highlight any text, a DM, an ad, a headline, and press one key. A local model names the persuasion technique being run on you, and can show where those words land in your cortex. Nothing you analyze ever leaves your Mac.

Read the code14 techniques · ~0.6s per scan · local LLM + Meta’s TRIBE v2 fMRI model
Litmus detecting a guilt-trip in a text message, with a cortical map of where it lands

A guilt-trip caught in a real Messages thread. The card names the technique, shows a confidence, and maps where it lands in cortex.

The one idea

Two models, each doing only what it is genuinely good at

The whole design falls out of one honest result from my own experiments: the brain model is a worse detector than the text it is built from, 100 percent versus 75 percent in a fair test, and information theory says it has to be. So detection is the language model's job. The brain map exists to make the manipulation felt, never to raise accuracy.

Detection · the verdict

A local language model names the technique

Llama 3.2 3B, 4-bit, running on Apple's MLX, scores the log probability of every candidate label and returns the most likely one with a confidence. About 0.6 seconds per press. It is the judge, and it stays resident.

Interpretation · the picture

TRIBE v2 shows where it lands in cortex

Meta's fMRI foundation model predicts cortical activation for the text, z-scored against a neutral baseline. It only sees cortex, so I say it engages your value and language cortex, never that it fires a fear center. It is loaded on demand and never used to detect.

Detection is two-stage

A gate first, then the label

A single classifier forced every input toward its nearest label, so a friendly "Hello!" came back as "Hype 84%". The fix is a clean yes or no gate that decides intent before anything gets named.

text -> [ stage 1 · is this manipulation at all? ] --no--> "no manipulation"
                     | yes
                     v
        [ stage 2 · which of 14 techniques? ] --> verdict + confidence

The 14 techniques split into two families: broadcast persuasion (fear, false urgency, FOMO, hype, authority, social proof, dopamine bait, outrage, tribal, awe) and interpersonal manipulation (guilt tripping, gaslighting, love bombing, blame shifting).

The math

No free-form guessing. Score every label, then compare.

I never let the model write a label and a number. Free generation collapsed distinct techniques into one and emitted a constant "80" for everything, a tic, not a probability. Instead I score every candidate label directly.

For a candidate label ℓ made of tokens t, given the prompt p, I take the length-normalized log-likelihood of the label under the model. Dividing by the token count keeps a two-token label from beating a one-token label just for being longer.

s(ℓ)=1∣ℓ∣∑i=1∣ℓ∣log⁡Pθ ⁣(ti∣p, t<i)s(\ell)=\frac{1}{|\ell|}\sum_{i=1}^{|\ell|}\log P_\theta\!\left(t_i \mid p,\,t_{<i}\right)
per-label score

Then a softmax over the labels turns those scores into a probability distribution. The argmax is the verdict, and the winner's probability is the confidence. A temperature below 1 sharpens the distribution, so a clear case reads assertively instead of hedged.

P(ℓ)=exp⁡ ⁣(s(ℓ)/τ)∑ℓ′exp⁡ ⁣(s(ℓ′)/τ),y^=arg⁡max⁡ℓ P(ℓ)P(\ell)=\frac{\exp\!\big(s(\ell)/\tau\big)}{\displaystyle\sum_{\ell'}\exp\!\big(s(\ell')/\tau\big)},\qquad \hat{y}=\arg\max_{\ell}\,P(\ell)
softmax + argmax

This is the part worth seeing. The raw scores for a real example sit close together, within about a nat of each other. The temperature is what pulls them apart into a confident pick:

guilt-tripping75%s=-0.9love-bombing22%s=-1.3blame-shifting2.0%s=-1.6fear-mongering0.6%s=-2.1none0.4%s=-2.4

Guilt-tripping "wow, after everything I've done for you." Raw scores s are close (left numbers); softmax with τ = 0.25 turns them into 75 vs 22 vs the rest.

The gate is the same trick with two labels, "yes" and "no", rather than the loaded word "manipulation" (repeating it biased the score toward itself). It reduces to a logistic on the score gap, and I flag anything above a swept threshold.

P(manip)=σ ⁣(s(yes)−s(no)τg),flag if P(manip)>1−θP(\text{manip})=\sigma\!\left(\frac{s(\text{yes})-s(\text{no})}{\tau_g}\right),\quad \text{flag if } P(\text{manip})>1-\theta
the gate

The shown percent can be Platt-calibrated so it actually means probability-correct, fit on held-out data, though it ships assertive by choice. And since the prompt prefix is shared across every label, I process it once into a KV cache and continue per label, a 9x speedup with identical verdicts.

p^=σ ⁣(a logit⁡(Pmax⁡)+b),σ(z)=11+e−z\hat{p}=\sigma\!\big(a\,\operatorname{logit}(P_{\max})+b\big),\qquad \sigma(z)=\frac{1}{1+e^{-z}}
calibrated confidence

One label is often too few. "Act now, only two left, everyone is buying" is urgency and scarcity and social proof at the same time. So next to the single winner I keep every technique whose probability clears a small floor, and show that set as the mixture.

M={ ℓ  :  P(ℓ)≥φ },φ=0.15\mathcal{M}=\big\{\,\ell \;:\; P(\ell)\ge \varphi\,\big\},\qquad \varphi = 0.15
the mixture

It also changes how you should score the whole thing. The fair question is not "which single label" but "did we name the manipulation at all," so I measure top-k: is the true technique among the k highest-ranked? The 14B already names the exact technique 82 percent of the time, and the scans it gets wrong are almost always ones where the true technique is its second pick, the other tactic genuinely present in the same sentence. Counting the top two recovers most of those, which projects the top-2 accuracy to around 90.

acc@k=1N∑n=1N1 ⁣[ yn∈Tk(xn) ]\text{acc@}k=\frac{1}{N}\sum_{n=1}^{N}\mathbf{1}\!\big[\,y_n \in \mathcal{T}_k(x_n)\,\big]
top-k accuracy

where Tₖ is the k labels with the highest probability, y is the true technique, and 𝟙 is 1 when it lands inside that set. Top-2 is the fair number, because a single-label score punishes the model for picking the other technique a human would argue for too.

The experiments

Every design decision was forced by a result, not a hunch

The brain map is not a better detector

A plain text embedding classifies emotion at 100 percent versus the brain map's 75 percent. The brain is a function of the text, so it can never carry more signal than the words. Detection stays with the language model.

There is no clean technique to region map

Fear, outrage, and reward all load on the same fronto-orbital regions (r around 0.85). So the tool never claims fear targets the amygdala. It reports the measured cortical impact instead.

A raw brain map is mostly acoustic

A single sentence's raw prediction is dominated by auditory and sensorimotor cortex, not meaning. Only a curated set of semantic regions recovers the content signal, and that is what the card shows.

Measured honestly

Most detectors quote accuracy on data the authors wrote. This one does not.

Every number here is on 300 independently labeled examples from public datasets, dark patterns, logical fallacies, and clickbait, that I neither wrote nor tuned on. The fast on-device model names the exact technique at 62 percent, and a bigger on-device model reaches 82. Most of the remaining misses are the second technique genuinely present in the same sentence, so counting the top two should push that to around 90.

82.3%
Technique accuracy
external · 14B · never tuned on
0.93
Gate PR-AUC
manipulation vs benign
65%
On fresh data
independent corpus · it transfers
100%
On device
no cloud · no telemetry

The gate tells manipulation from benign very well, PR-AUC 0.93 and recall 0.78. It got there by a diagnostic that traced its misses to e-commerce dark patterns it was reading as plain facts, "24 people bought this today", bare countdown timers, then teaching the gate each family while guarding the benign look-alikes. That lifted recall from 0.56 to 0.78 with no precision cost.

Naming the technique was the next wall. Stage two was dumping everything into false urgency, so I gave it a rule that routes by the primary lever the text pulls: limited supply is FOMO, a deadline clock is urgency, other people's activity is social proof, a reward or curiosity hook is dopamine, a name or credential is authority. Accuracy went from 44 to 62 percent, and it transfers to a completely independent corpus at 65 percent, so it learned the patterns, not the phrasings.

I then tried two more levers, few-shot examples and a LoRA fine-tune, and both landed at the same 62 percent, statistically tied. So the real lever was a bigger local model, and it climbs monotonically: 8B to 69 percent, 14B to 79, and the 14B with few-shot examples to 82, all still on the Mac.

Built to defend attention, not harvest it

Which is why it runs entirely on your machine. The whole thing is open: a Swift menu bar app talking to a local Python server, and the experiment record behind every claim.