- readsword frequency + your own reviews
- becomesthe forgetting curve → decaying mass; review re-binds
- writes
--w - you seeunreviewed words fade in real time; a review springs the word back
No canvas is drawn here — the field is invisible; these variables are its only output. Without it you would build: a spaced-repetition scheduler and its timers.
field-ui · invisible fields · memory
Knowledge decays. Anchors hold.
Every fact you learn starts fading the moment you look away — but how fast depends on how deeply it's anchored. These 96 words carry real frequencies from the Google Web Trillion Word Corpus, and frequency is the anchor here: a word the world uses fifty million times is held by everything around it; a rarer one slips first.
Drag days since review and the whole grid decays along an Ebbinghaus-shaped curve — each card's retention is its anchor strength times an exponential whose half-life grows with that strength, so the strongest words visibly hold longest. Click a card to review it: it springs back to full at the current day and re-decays as you push the slider further. The field is invisible — retention shows only in type weight, ink, and anchor.
size = retention — what's still held at the current day · anchor = corpus frequency — common words decay slower
- Aa size = retention at the current day
- anchor = frequency (deeper = slower decay)
- click a card to review — it springs back, then re-decays
- the ink is live — --w is the retention math and --cat the accent; --d is the engine's local density (hold a card and the field gathers) and --field-attention the recipe's eased attention as you scroll. The live channels touch only ink — retention stays pure data.
How it's built
Every card is a <button> carrying its anchor strength as a data
attribute. The slider drives one Ebbinghaus-shaped formula across all 96 cards;
each result is written to --w and data-strength, and CSS turns it
into type heft and ink. Reviews are a per-card timestamp — nothing more. Alongside the
curve sit two live channels the engine writes back every frame — --d (local
density, gathered by data-hot on hover) and --field-attention
(the recipe's eased attention) — which touch only ink; retention stays pure data math.
1 — mark each word as a field body
<!-- each word is a body. data-feedback: the engine writes
--d (live local density) back every frame. data-hot:
hover/focus a card and the field gathers toward it. -->
<button
data-body="attract"
data-strength="1.62"
data-feedback
data-hot
data-anchor="0.91"
style="--w: 0.76; --cat: #4da3ff;"
>
republic
</button> data-body="attract"— registers as a field participantdata-anchor— normalized corpus frequency (the anchor)data-strength— mass, recomputed from retentiondata-feedback— opt in to--field-*writebacks (--d)data-hot— hover/focus gathers the field toward this card
2 — the forgetting curve, and where reviews live
// the world-time kernel — Ebbinghaus
// retention with a stability term:
// w = a·exp(−since/τ(a)), τ = 4 + a·56 days
import { retention, DAY_MS } from "@fundamental-engine/core";
// a = zipf, normalized 0.15..1
// (real Trillion Word Corpus counts)
w = retention(a, days_eff * DAY_MS);
// deep anchors decay slower — τ grows with a
// review: per-card timestamp
days_eff = max(0, slider − reviewedAt)
// slider input → every card's --w
// and data-strength update live
// the scoped field runs invisible (renderless)
// and asks for the attention metric lane —
// the pipeline writes --field-attention per
// card (eased 0..1):
field = applyRecipe(grid, base, {
bodies: cards,
annotateBodies: false,
renderless: true,
extraMetrics: ["attention"],
}); // reviews persist on this device — localStorage,
// not an account:
"fui:memory-reviews" → { word: reviewedAtDay }
"fui:memory-day" → the slider's day
// restore on load: only words present on the
// page; the slider + decay render from the
// stored day. "reset reviews" clears both keys.
// Storage reads/writes never throw — a private
// window simply doesn't remember. The shape is Ebbinghaus's: exponential decay with a stability term. The frequencies are real; the curve's constants are tuned for legibility, not fitted to recall data. Reviews and the slider's day persist in this device's localStorage — close the tab, come back, and the field remembers what you reviewed.
3 — CSS reads the retention + the live channels
.mx-card {
/* --w = retention (pure data math — the curve)
--d = live engine density, data-hot gathers it
--field-attention = the recipe's eased attention */
--live: var(--d, 0);
opacity: calc(0.58 + var(--w) * 0.42);
box-shadow: 0 0 calc(var(--live) * 16px) -6px
color-mix(in srgb,
var(--cat) calc(var(--live) * 55%), transparent);
}
.mx-word {
font-variation-settings:
'wght' calc(380 + var(--w) * 360);
}
/* a reviewed card's ring breathes with the density */
.mx-card[data-reviewed] {
outline: 2px solid color-mix(in srgb,
var(--cat) calc(55% + var(--live) * 35%),
transparent);
}
One variable in, the whole presentation out — and with
prefers-reduced-motion set, the cards skip transitions entirely.