My App
LoopKit

Architecture

Design principles and data flow of the LoopKit flashcard engine

Design Principles

LoopKit is built around five core principles:

Pluggable Storage

All database operations go through the abstract LoopKitStorage class. This means you can use any database by implementing the storage interface. A Prisma adapter is included out of the box.

Swappable SRS Algorithms

The SM-2 algorithm is the default, but you can implement the SRSAlgorithm interface to use any spaced repetition algorithm (FSRS, Leitner, custom).

Headless-First Frontend

Every React hook exposes the full state and actions. Pre-styled components are optional wrappers around these hooks — use them for rapid prototyping or build your own UI.

API-Driven Integration

The backend exposes services that your API controllers consume. The frontend communicates exclusively through HTTP — there are no direct database calls from the client.

Zero Monolithic Dependencies

The storage interface uses structural typing. The backend package never imports Prisma directly — the adapter handles all ORM-specific logic.

Data Model

NoteType (template definition)
  ├── FieldDef[] (field schema)
  └── TemplateDef[] (front/back templates)

Note (study material)
  ├── Field[] (field values)
  └── tags: string[]

Card (generated from Note + Template)
  ├── SRS state (ease, interval, due date)
  └── belongs to a Deck

Deck (collection of cards)
  ├── parent/child hierarchy
  ├── preset or config overrides
  └── DeckCounts (new, learning, review, relearning)

ReviewLog (immutable review history)
  ├── previous card state snapshot
  └── new card state snapshot

Data Flow

Card Creation

  1. Define a NoteType with fields and templates
  2. Create a Note with field values
  3. CardGenerator produces one Card per template
  4. Cards start in new state with initial SRS parameters

Review Session

  1. ReviewSessionService.buildQueue() collects due cards
  2. Queue ordering: learning/relearning first, then review (most overdue), then new
  3. Daily limits enforced: newCardsPerDay and maxReviewsPerDay
  4. User grades each card: again, hard, good, or easy
  5. SM-2 algorithm calculates next state (interval, ease factor, due date)
  6. ReviewLog created for each grade action (immutable audit trail)

Card State Machine

        ┌─── again ───┐
        ▼              │
new → learning ──good──→ review ◄──good── relearning
        │                  │                   ▲
        └── easy ──────────│                   │
                           └─── again ─────────┘
  • new: Never studied. Enters learning on first review.
  • learning: Working through learning steps (e.g., 1min, 10min). Graduates to review on good after all steps.
  • review: Mature card. Interval grows with each successful review.
  • relearning: Lapsed card (answered again during review). Works through relearning steps before returning to review.

Configuration Inheritance

DEFAULT_DECK_CONFIG (hardcoded defaults)
  └── DeckPreset (named configuration template)
       └── Deck.configOverrides (per-deck overrides)

DeckService.getEffectiveConfig() merges these layers to produce the final configuration for a deck.

On this page