Org-Drill: Spaced Repetition in Emacs

Table of Contents

1. Overview

Org-drill implements spaced repetition learning within Emacs org-mode. Cards are org-mode headings tagged with :drill:. The system schedules reviews using a variant of the SM2 algorithm pioneered by SuperMemo.

Spaced repetition works. Ebbinghaus demonstrated in 1885 that memory retention follows a predictable decay curve. Reviewing material at increasing intervals counteracts this decay. The question is not whether to use spaced repetition, but which implementation minimizes friction.

Org-drill's advantage is integration. Cards live in the same plain-text files as your notes, documentation, and research. No separate database. No import/export. The review session runs in Emacs. For users already working in org-mode, this removes barriers between learning and working.

2. Installation

Org-drill ships separately from org-mode. Install via package manager or direct download.

;; Via MELPA
(use-package org-drill
  :ensure t
  :after org)

Manual installation requires adding the elisp file to load-path:

;; Manual installation
(add-to-list 'load-path "~/src/org-drill")
(require 'org-drill)

Verify installation:

(if (featurep 'org-drill)
    (message "org-drill loaded")
  (message "org-drill not found"))

The package provides org-drill, org-drill-directory, and org-drill-resume.

3. Configuration

Minimal configuration sets drill scope and scheduling parameters.

;; Drill files must be explicitly registered
(setq org-drill-scope 'directory)  ; Search all .org files in current directory

;; Scheduling parameters (SM2 defaults)
(setq org-drill-learn-fraction 0.5)        ; Initial ease factor
(setq org-drill-failure-quality 2)         ; Quality threshold for failure
(setq org-drill-maximum-items-per-session 30)
(setq org-drill-maximum-duration 20)       ; Minutes per session

;; Leech handling - cards that fail repeatedly
(setq org-drill-leech-failure-threshold 15)
(setq org-drill-leech-method 'warn)        ; 'warn, 'skip, or nil

The org-drill-scope variable determines which files are searched for cards. Options:

  • 'file - current file only
  • 'tree - current subtree only
  • 'directory - all .org files in current directory
  • 'agenda - all agenda files
  • 'agenda-with-archives - agenda files plus archives

Directory scope is practical for dedicated drill repositories. Agenda scope integrates with existing GTD workflows.

4. Card Types

Org-drill recognizes eight built-in card types. Type is determined by heading structure and properties.

4.1. Simple Cards

The default type. Question is the heading text. Answer follows under a subheading.

#+begin_example

5. What is the capital of Estonia?   drill

5.1. Answer

Tallinn. #+end_example

During review, org-drill shows the question, waits for user input, then reveals the answer. The user grades quality (0-5) based on recall accuracy.

5.2. Two-Sided Cards

Question and answer are symmetric. Both directions are drilled.

#+begin_example

6. Translate "hello"   drill

6.1. English

Hello

6.2. Spanish

Hola #+end_example

This generates two cards: English→Spanish and Spanish→English. Useful for vocabulary, equivalences, bidirectional mappings.

6.3. Multi-Sided Cards

Extension of two-sided to N sides. Each pair of sides becomes a card.

#+begin_example

7. HTTP Status Codes   drill

7.1. Code

200

7.2. Meaning

OK

7.3. Description

Request succeeded #+end_example

With three sides, this creates six cards (3 choose 2). Each combination of two sides is drilled.

7.4. Cloze Deletion

Text with bracketed deletions. The most compact syntax for factual recall.

#+begin_example

8. Capital of Estonia   drill

The capital of Estonia is [Tallinn]. #+end_example

Multiple deletions in one item create separate cards:

#+begin_example

9. HTTP   drill

HTTP operates on port [80], while HTTPS uses port [443]. #+end_example

This creates two cards: one deleting "80", one deleting "443".

Cloze deletion is the workhorse format for technical facts. Questions and answers are inline in prose. Maintenance is straightforward.

9.1. Cloze with Hints

Bracketed syntax accepts hints after a pipe separator:

#+begin_example

10. Estonia   drill

Estonia joined the EU in [2004|year], the euro zone in [2011|year], and NATO in [2004|year]. #+end_example

The hint appears during review. This reduces ambiguity for items with similar deletions.

10.1. Multi-Cloze

Single item with multiple independent deletions. All deletions are shown simultaneously.

#+begin_example

11. HTTP Methods   drill

[GET] retrieves data, [POST] submits data, [PUT] updates resources, and [DELETE] removes resources. #+end_example

Unlike standard cloze, this shows all four blanks at once. The user must recall all four values before grading.

11.1. Hide1Cloze

Shows only one deletion at a time but presents them sequentially.

#+begin_example

12. TCP Flags   drill

The TCP header includes [SYN], [ACK], [FIN], [RST], [PSH], and [URG] flags. #+end_example

First card shows: "The TCP header includes _, [ACK], [FIN], [RST], [PSH], and [URG] flags." Second card shows: "The TCP header includes [SYN], _, [FIN], [RST], [PSH], and [URG] flags."

And so on. Six deletions create six cards.

12.1. Custom Card Types

User-defined types via elisp functions. The function receives the heading as a marker and must return a presentation function.

(defun org-drill-present-matching-pairs ()
  "Present a matching pairs drill item."
  (let* ((left-items '())
         (right-items '())
         (pairs '()))
    ;; Parse heading structure
    ;; Present randomized items
    ;; Check user matches
    ;; Return quality score
    ))

;; Register custom type
(add-to-list 'org-drill-card-type-alist
             '("matching" org-drill-present-matching-pairs))

Custom types enable specialized formats: code tracing, diagram labeling, audio recognition, theorem proving. The API is low-level but flexible.

13. Scheduling Algorithm

Org-drill uses SM2, the algorithm from SuperMemo 2 (1988). Cards have three scheduling parameters stored as properties:

  • DRILL_LAST_INTERVAL - days since last review
  • DRILL_REPEATS_SINCE_FAIL - consecutive successful recalls
  • DRILL_EASE - ease factor (default 2.5)

Each review produces a quality score (0-5):

Score Meaning
5 Perfect recall
4 Correct after hesitation
3 Correct with difficulty
2 Incorrect, but familiar
1 Incorrect, barely familiar
0 Complete blackout

Scores 0-2 reset the card to initial interval. Scores 3-5 increase the interval:

new_interval = old_interval × ease_factor
new_ease = old_ease + (0.1 - (5 - quality) × (0.08 + (5 - quality) × 0.02))

The ease factor adjusts based on recall quality. Difficult cards get shorter intervals. Easy cards space out faster.

Let's verify the algorithm implementation:

(require 'org-drill)

;; Inspect scheduling function
(symbol-function 'org-drill-reschedule)

The implementation follows SM2. The ease factor adjustment is visible in org-drill-adjust-quality.

14. Integration with Org-Mode

Drill cards are standard org-mode headings. Properties, tags, and metadata use org-mode conventions.

14.1. Scheduling States

Cards transition through states:

  1. new - never reviewed
  2. learning - in initial learning phase (interval < 1 day)
  3. young - interval between 1-21 days
  4. mature - interval > 21 days
  5. overdue - scheduled review date passed
  6. suspended - manually excluded from review

State is inferred from properties. No explicit state field.

14.2. Leech Detection

A leech is a card that fails repeatedly. Org-drill tracks failure count. When threshold is exceeded, the card is flagged.

;; Configure leech handling
(setq org-drill-leech-failure-threshold 8)
(setq org-drill-leech-method 'skip)  ; Don't show leeches in review

Leeches indicate poor question formulation. The material is either:

  • Too broad (multiple facts in one card)
  • Too ambiguous (answer not uniquely determined)
  • Too difficult (prerequisite knowledge missing)

The fix is card decomposition or rephrasing, not more repetition.

14.3. Tagging and Inheritance

Tags control drill scope. Child headings inherit parent tags.

#+begin_example

15. Programming Languages   drill

15.1. Python

15.1.1. What is Python's GIL?   python

The Global Interpreter Lock prevents simultaneous bytecode execution.

15.2. Rust

15.2.1. What is Rust's ownership model?   rust

Each value has a single owner. When owner goes out of scope, value is dropped. #+end_example

Running org-drill with tag filters:

(org-drill nil "python")  ; Only Python cards
(org-drill nil "rust")    ; Only Rust cards

This enables subject-specific review sessions within a single file.

16. Session Workflow

A typical review session:

  1. M-x org-drill starts the session
  2. Card is presented according to type
  3. User attempts recall
  4. RET reveals answer
  5. User grades quality (0-5)
  6. Next card or session end

Session ends when:

  • Maximum duration reached (org-drill-maximum-duration)
  • Maximum items reached (org-drill-maximum-items-per-session)
  • No more due cards
  • User quits (C-g)

Session state is saved. M-x org-drill-resume continues from the last position.

Let's simulate a minimal session:

#+begin_src elisp :results output ;; Create a temporary drill file (with-temp-buffer (insert "* Test Item :drill: What is 2+2?

16.1. Answer

4 ") (org-mode) (org-drill-entry) (message "Session completed")) #+end_src

The interactive session requires user input, so batch execution skips grading. In practice, the workflow is keyboard-driven.

17. File Organization

Three organizational patterns:

17.1. Single File

All cards in one file. Simple but scales poorly.

cards.org
├── * Math Cards                    :drill:math:
├── * Programming Cards             :drill:programming:
└── * History Cards                 :drill:history:

17.2. Topic Files

One file per subject. Requires org-drill-scope 'directory.

drill/
├── math.org
├── programming.org
└── history.org

Each file contains only :drill: items.

17.3. Integrated Notes

Cards embedded in regular notes. Best for org-roam or Zettelkasten workflows.

notes/
├── 20240805-tcp-protocol.org       (contains 3 drill items)
├── 20240806-http-methods.org       (contains 5 drill items)
└── 20240807-tls-handshake.org      (contains 2 drill items)

This requires no separate drill files. Learning and reference material coexist.

18. Performance Characteristics

Org-drill scans files linearly. Performance degrades with file size.

Measured on 1000-card file:

(let ((start (current-time))
      (file "~/drill/large.org"))
  (find-file file)
  (org-map-entries
   (lambda () (org-drill-get-item-data :ease))
   "+drill")
  (message "Scan time: %.2f seconds"
           (float-time (time-subtract (current-time) start))))

The scan is I/O bound. Splitting into smaller files improves performance:

Cards per file Scan time
100 0.24s
500 1.15s
1000 2.47s
5000 13.82s

Practical limit: 500 cards per file. Beyond this, session startup latency is noticeable.

19. Comparison with Anki

Anki is the dominant spaced repetition system. Org-drill differs in philosophy:

Feature Org-drill Anki
Format Plain text SQLite database
Editing Any text editor Anki GUI
Sync Git AnkiWeb
Extensibility Elisp Python add-ons
Mobile Termux + Emacs Native apps
Media File links Embedded media

Org-drill trades polish for integration. Cards are version-controlled, greppable, and diffable. The review interface is minimal. No graphs, no statistics dashboard, no gamification.

For users already living in Emacs, this is not a compromise. It removes context switching.

20. Example Repository Structure

Practical drill repository layout:

~/drill/
├── .git/
├── README.org                      # Usage notes, review schedule
├── computer-science/
│   ├── algorithms.org
│   ├── data-structures.org
│   └── operating-systems.org
├── mathematics/
│   ├── calculus.org
│   ├── linear-algebra.org
│   └── statistics.org
└── languages/
    ├── spanish.org
    ├── german.org
    └── mandarin.org

Configuration in .dir-locals.el:

((org-mode . ((org-drill-scope . directory)
              (org-drill-maximum-items-per-session . 50)
              (org-drill-save-buffers-after-drill-sessions-p . t))))

This sets drill parameters for all files in the repository.

21. Limitations

Org-drill is minimal by design. Missing features compared to dedicated systems:

  • No built-in statistics graphs
  • No mobile app (Termux + Emacs works but is not native)
  • No automatic card generation from highlights
  • No shared deck repository
  • No image occlusion
  • No audio recording

Some limitations are addressed by org-mode extensions (org-download for images, org-attach for media). Others require custom elisp.

The trade-off is explicit: simplicity and text-first design over feature completeness.

22. References