Retain

approvedunreviewed

by sejder

Spaced-repetition flashcards from simple Question::Answer lines, scheduled with FSRS. All review data stays out of your notes.

Updated todayMIT

Retain

Spaced-repetition flashcards for Obsidian that stay out of your notes.

Write cards as plain Question::Answer lines. Retain schedules them with FSRS, the algorithm modern Anki uses. All review data lives in the plugin's own data file, so your notes never get scheduling comments or IDs appended to them.

Deck list

Reviewing a card with LaTeX, showing interval previews on the grade buttonsStats: reviews per day, retention, due forecast
Review with LaTeX, links and interval previewsStats: history, retention, upcoming reviews

Features

  • Clean notes. Nothing is ever written into your notes. If you edit a question, Retain recognizes the edited card and keeps its history.
  • FSRS scheduling, with Again / Hard / Good / Easy and a configurable target retention.
  • Decks from tags. Tag a heading with #flashcards/languages/spanish and every card under it goes into that deck. Nested tags become nested decks.
  • Keyboard-driven reviews: Space shows the answer, 1–4 grade it, U undoes.
  • Cram mode goes through a whole deck without touching its schedule.
  • Edit in place from the review screen, with a live rendered preview of the LaTeX and markdown as you type. Or jump to the card in its note.
  • Daily new-card limit per deck.
  • Stats: reviews per day, true retention and a 30-day due forecast.
  • Status bar count of cards due.
  • Works on mobile, with a full-screen layout and large touch targets.

Reviewing on a phone    Deck list on a phone

Editing a card with live LaTeX preview

Writing cards

Put the flashcard tag on a heading, then write one card per line with :: between question and answer:

## Vocabulary #flashcards/languages/spanish
la biblioteca::the library
madrugar::to get up early

## Mechanics #flashcards/science/physics
Newton's second law?::$F = ma$
Period of a simple pendulum?::$T = 2\pi\sqrt{L/g}$
  • The heading's tag applies until the next heading of the same or a higher level.
  • If the tag is in the frontmatter or a body line instead, it applies to the whole note.
  • #flashcards on its own puts cards in a deck called default.
  • Lines inside code blocks and $$ math blocks are ignored, and so are :: inside inline code.
  • Lines outside a tagged scope are never treated as cards, so Dataview key:: value fields are safe.
  • List items work too, e.g. - question::answer.

The tag, the separator and the scheduling options are under Settings → Retain, and they also show up in Obsidian's settings search.

Where your data lives

Review state is stored in .obsidian/plugins/retain/data.json, keyed by a hash of each card's question:

You do thisRetain does this
Fix a typo in a questionMatches the edited card to the old one and keeps its history
Rewrite a question completelyTreats it as a new card. The old card's history is kept for 30 days in case it comes back
Edit via the ✏️ button in a reviewMoves the history over directly, however much the question changed
Rename or move a note, or move a card to another noteKeeps the history
Delete a cardKeeps its history for 30 days (in case you undo), then removes it

The data file syncs with the rest of your .obsidian folder (Obsidian Sync, iCloud, Nextcloud, git…).

Privacy

Retain makes no network requests. To build the card index, it lists the markdown files in your vault and reads only the notes that carry your flashcard tag, using Obsidian's metadata cache to tell them apart. Nothing leaves your device.

Commands

Command
Review flashcardsOpens the deck list (also on the ribbon icon and the status bar)
Review flashcards in current noteStarts a review session for the open note
Open stats
Rescan vaultRebuilds the card index

In a review: Space/Enter shows the answer, then grades Good · 1–4 grade · U or Ctrl/Cmd+Z undo · E edit · O open in note · Esc close.

Settings

SettingDefault
Flashcard tag#flashcards
Separator::
New cards per day (per deck)20
Target retention0.90
Maximum interval36500 days
Fuzz intervalson
Show interval previewson

Installation

From Obsidian: Settings → Community plugins → Browse → search for "Retain" (once it's accepted into the community list).

With BRAT: add Sejder/retain.

Manually: download main.js, manifest.json and styles.css from the latest release into <vault>/.obsidian/plugins/retain/, then enable the plugin under Community plugins.

Requires Obsidian 1.13 or later.

GitHub Actions builds release files from the tagged source and signs them with build provenance attestations. To check a download:

gh attestation verify main.js --repo Sejder/retain

Development

The repo includes a devenv environment. Entering the directory activates it and installs the dependencies. Without devenv, any Node 22 works.

npm run dev      # rebuild on change and copy into $VAULT/.obsidian/plugins/retain
npm test         # unit tests (parser, card store, queue)
npm run build    # type-check + production bundle

To release, bump the version in manifest.json, package.json and versions.json, then push a matching tag (e.g. git tag 0.2.0 && git push --tags). The release workflow tests, builds, attests and publishes it.

Set VAULT to your test vault, for example in a devenv.local.nix containing { env.VAULT = "/path/to/vault"; }. That file is gitignored. With VAULT set, npm test also parses your vault as a sanity check.

The screenshots come from the demo vault in demo/vault. To regenerate them, run Obsidian on a virtual display:

npm run build && npm run demo:seed && npm run demo:deploy
Xvfb :97 -screen 0 1600x1000x24 & DISPLAY=:97 node demo/screenshots.mjs docs-raw

The script also checks the UI: exactly one close button, nothing overflowing, undo and edit working, and no console errors.

License

MIT

For plugin developers

Search results and similarity scores are powered by semantic analysis of your plugin's README. If your plugin isn't appearing for searches you'd expect, try updating your README to clearly describe your plugin's purpose, features, and use cases.