Org-Drill: Spaced Repetition in Emacs
Table of Contents
- 1. Overview
- 2. Installation
- 3. Configuration
- 4. Card Types
- 5. What is the capital of Estonia? drill
- 6. Translate "hello" drill
- 7. HTTP Status Codes drill
- 8. Capital of Estonia drill
- 9. HTTP drill
- 10. Estonia drill
- 11. HTTP Methods drill
- 12. TCP Flags drill
- 13. Scheduling Algorithm
- 14. Integration with Org-Mode
- 15. Programming Languages drill
- 16. Session Workflow
- 17. File Organization
- 18. Performance Characteristics
- 19. Comparison with Anki
- 20. Example Repository Structure
- 21. Limitations
- 22. References
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 reviewDRILL_REPEATS_SINCE_FAIL- consecutive successful recallsDRILL_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:
new- never reviewedlearning- in initial learning phase (interval < 1 day)young- interval between 1-21 daysmature- interval > 21 daysoverdue- scheduled review date passedsuspended- 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:
M-x org-drillstarts the session- Card is presented according to type
- User attempts recall
RETreveals answer- User grades quality (0-5)
- 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
- Simple card example
- Cloze deletion example
- Two-sided card example
- Multi-cloze example
- Cloze with hints example
- Multi-sided card example
- Explain card type
- Custom card type
Source code: https://gitlab.com/phillord/org-drill
SM2 algorithm: Wozniak, P. (1990). "Optimization of learning". University of Technology in Poznan.