← BackBehavioral PWA · 2026

Inner Light

Every moment of awareness is a light you give yourself

Product Design · Behavioral Science · Vanilla Build · 2026

Open the live prototype
Inner Light

What it is

A one-tap PWA built directly on David D. Burns's work in Feeling Good: The New Mood Therapy, the foundational CBT text that codified the ten cognitive distortions and made 'identify the distortion' the core technique for recovering from depression. Five of those distortions live in the app, Self-criticism, Comparison, Catastrophizing, Should-ing, Doubting, and each catch adds a particle of warm light to a quiet counter. A later pass added an optional one-line note, which turned the app from a counter into something that can tell you what you have actually been circling, and which worries have settled.

Problem

Naming a thought loosens its grip. Most apps put a paragraph in the way.

David D. Burns, in Feeling Good: The New Mood Therapy, codified one of the most-replicated findings in CBT: simply naming a cognitive distortion the moment you catch it loosens its grip on mood, before any rewrite or reframe. Most journaling apps bury that single move under worksheets and forms. Inner Light strips it back to two taps, five seconds, done. The second version adds back exactly one optional line, because stripping it that far left the app with nothing to tell you afterwards.

Five fixed categories. No taxonomy to design.

Five fixed categories. No taxonomy to design.

Self-criticism, Comparison, Catastrophizing, Should-ing, Doubting, the five patterns CBT clinicians return to most. Pick one. The constraint is the design: no custom tags, no setup, no decision fatigue.

One tap to catch, one to undo

One tap to catch, one to undo

Catch a thought and a particle of warm light appears, the counter climbs, an undo chip lingers for a few seconds. No save button, no commit step. The whole interaction is reversible until you scroll past it.

The reward is the constellation, not the chart

The reward is the constellation, not the chart

Each catch is a single point of light drifting at the center of an otherwise empty screen. After a few days the zero quietly becomes a cluster of stars. No streaks, no graphs, visual reinforcement for noticing itself.

Even the review screen refuses the chart

Even the review screen refuses the chart

Seven days as seven orbs, sized and lit by that day's count rather than plotted against an axis. The same language as the main screen, carried into the place where most apps switch to measurement.

Process · Decision Points

The hard calls, and what they cost.

Two calls from the second pass, both about how much you can add to an app whose whole premise was subtraction.

Decision · 01

Does a counter that tells you nothing still earn its place?

After using the first version for a while, the pattern was clear and useless. A number, and a bar chart of which distortion I reach for most. It could not answer the only question I actually had, which was what I had been going round and round about. Five labels describe the shape of a thought, never its subject. The fix had to not cost the thing that made the app work, so it is one optional line, 140 characters, sitting above the button rather than in front of it. Skip it and the app behaves exactly as version one did. The field is a place to put something, not a gate to get past.

Considered and dropped

Leave it as a counter

Honest to the original constraint, and it means a month of practice produces a tally and no recall. The data was there and the app refused to use it.

A real journal field

Multi-line, with a save step. It answers the question and it reintroduces the exact friction that kills the Daily Mood Log.

Does a counter that tells you nothing still earn its place?, Built. One optional line

Built. One optional line

'What was it about?' sits above the button, capped at 140 characters, and never blocks the catch. The chips underneath are worries still open in the last three days, so a recurring thought costs one tap and no typing.

Trade-off

It is still a field, and a field is still a decision to make while a thought is passing. Catches with no note are now second-class in every review surface, which means the app quietly pulls toward the slower path it was built to avoid.

Decision · 02

Group the notes by keyword, or by the worry itself?

Keywords are the obvious move and they produce the wrong object. 'No reply to my message yet' surfaced nine times in one day; as tags that is nine entries sharing a word, and as a worry it is one thing that recurred and then resolved in a single moment. So each note joins an open worry when it is plainly about the same thing, and otherwise opens a new one. Matching runs on content words with stopwords stripped, and overlap is measured against the shorter of the two notes so a terse re-entry still finds its original. One shared word is a coincidence, so two are required before anything merges. The threshold is deliberately conservative: a wrongly split worry is a duplicate line, while a wrongly merged one quietly rewrites what you were worried about.

Considered and dropped

User-managed tags

Accurate by definition, and it is filing. Asking someone to maintain a taxonomy of their own intrusive thoughts is the homework the app exists to remove.

An LLM to cluster them

Better matching, at the cost of the two properties the app is built on: it works offline, and nothing ever leaves the phone.

Group the notes by keyword, or by the worry itself?, Built. One line per worry

Built. One line per worry

Open worries at the top, each with a checkbox. Check one off and it strikes through as settled. If it comes back after you closed it, it reappears unstruck, which is more truthful than keeping it shut.

Group the notes by keyword, or by the worry itself?, Built. Distortions demoted

Built. Distortions demoted

'Kinds of thought' still sits underneath, now as the secondary read. The subject became the primary one.

Trade-off

Word overlap is a crude proxy for sameness. Two notes about different things that share vocabulary will merge, the same worry described in fresh words will split, and there is no way to join or separate worries by hand yet.

Over time

The empty zero becomes a constellation

0, Day one

0, Day one

A few catches in

A few catches in

82, A few weeks in

82, A few weeks in

Key Insight

Friction kills the catch, and so does a catch that tells you nothing

Burns's 'Identify the Distortion' technique has decades of CBT outcome research behind it, yet most readers of Feeling Good never sustain the practice, because the recommended Daily Mood Log takes too long in the moment. Every additional surface, a save button, a tag picker, a journal field, is a place where the impulse to label dies. That argument built version one and it is still right. What it missed is the other end: an app cheap enough to use every day for a month and then unable to tell you what the month was about gives you no reason to open it in week five. The second version is one optional line wide, and the whole design problem was keeping it optional.

Second version

Three views, because they answer three different questions

The history panel splits into Week, Timeline and Topics. Week is how much, and keeps the orb language rather than switching to a graph. Timeline is what exactly, every catch in order with its note, editable in place because the first wording of a thought is rarely the accurate one. Topics is what keeps coming back, and it is the view the second version exists for. Tapping a worry or a distortion filters the timeline down to it. Everything still lives in localStorage on the device, with no account and no server, and entries saved by the first version are migrated forward on load rather than left behind.

Stack & Tools

design

Figma

code

Vanilla HTML / CSS / JSAlbert SansService WorkerWeb App Manifest

infra

localStorageDropbox static hosting

Open question

Does naming a distortion in a single tap sustain a daily practice where a written log does not?

What the data shows

How many people come back after day one, how many catches a session holds, and whether the optional note is used or skipped.

What it does not

It cannot show whether catching thoughts changes mood. That needs a measure taken before and after, not usage data.

More client and product design case studies at hannadevries.com