Chat about this codebase

AI-powered code exploration

Online

Project Overview

Org-srs embeds a spaced-repetition system directly into Org mode, letting you review flashcards, clozes and other item types without leaving Emacs. It adapts to your workflow, supports multiple algorithms (including FSRS), and scales from single-file study notes to large, tagged collections.

Value Proposition

  • Keep reviews and notes side by side in Org buffers
  • Leverage Org’s outlining, tagging and metadata for fine-grained scheduling
  • Customize review units (headlines, list items, blocks) and algorithms
  • Automate batch operations: bulk item creation, export/import, review statistics
  • Extend with Emacs Lisp hooks and Org properties

Feature Summary

  • Review item types:
    • Flashcards (question ↔ answer pairs)
    • Cloze deletions (mask spans within text)
    • Custom units via org-srs-define-review-unit
  • Algorithms:
    • Built-in SM-2 and SuperMemo variants
    • FSRS (Fast, Self-Adjusting) integration
    • Hook into other schedulers
  • Workflow commands:
    • org-srs-review-start to run a session
    • org-srs-item-create / DWIM variants for on-the-fly item insertion
    • Bulk updates (org-srs-cloze-update, org-srs-update-all)
  • Reporting and export:
    • Session logs and performance charts
    • Export to Anki, CSV, JSON

Licensing

Org-srs is distributed under the GNU General Public License version 3.0 (GPLv3). You may freely use, modify and redistribute under the following conditions:

  • Include the full text of GPLv3 in LICENSE at the repository root
  • Preserve license headers in all source files
  • Disclose source when distributing binaries (or provide a written offer)

For full terms, see LICENSE or https://www.gnu.org/licenses/gpl-3.0.html.

Supported Versions

  • Emacs 26.1 or later
  • Org mode 9.3 or later

Ensure your configuration loads Org-srs after Org-mode:

(use-package org-srs
  :after org
  :custom
  (org-srs-directory "~/org/srs/")
  (org-srs-review-method 'fsrs)
  :bind (("C-c r s" . org-srs-review-start)))
## Getting Started

Get Org-srs up and running in three steps: install, configure, then run your first review.

### Installation

Install from MELPA or Git:

• MELPA  
  1. M-x package-refresh-contents  
  2. M-x package-install RET org-srs RET  

• use-package  
```elisp
(use-package org-srs
  :ensure t
  :after org)

• Manual
Clone the repo and add to your load-path:

(add-to-list 'load-path "~/path/to/org-srs")
(require 'org-srs)

Basic Configuration

By default Org-srs scans org-agenda-files. You can override:

;; Directory holding your SRS notes
(setq org-srs-directory "~/org/srs/")

;; Files to scan for SRS items
(setq org-srs-files
      (directory-files-recursively org-srs-directory "\\.org$"))

;; Persist learning data
(setq org-srs-data-file
      (expand-file-name "srs-data.el" org-srs-directory))

;; Enable Org-srs in Org buffers
(add-hook 'org-mode-hook #'org-srs-mode)

;; Keybindings for rating during review
(with-eval-after-load 'org-srs
  (define-key org-srs-review-mode-map (kbd "e") #'org-srs-review-rate-easy)
  (define-key org-srs-review-mode-map (kbd "g") #'org-srs-review-rate-good)
  (define-key org-srs-review-mode-map (kbd "h") #'org-srs-review-rate-hard)
  (define-key org-srs-review-mode-map (kbd "a") #'org-srs-review-rate-again))

Creating Your First SRS Item

You can author flashcards, clozes or custom units inline.

Flashcard Example

Add this to ~/org/srs/flashcards.org:

* Vocabulary: Epitome
  :PROPERTIES:
  :SRS_TYPE: card
  :END:
** Front
What word describes “a perfect example”?
** Back
Epitome

Or interactively:

  1. Place point on an Org heading.
  2. M-x org-srs-item-create RET card RET
  3. Fill any prompted properties (e.g. tags, initial schedule).

Cloze Example

Within any Org heading:

Here’s Newton’s second law: {{e5f3a1b}{F = m · a}{force}}

To create/remove cloze at point:

  • M-x org-srs-item-cloze-dwim
  • Regenerate all IDs after big edits: M-x org-srs-item-cloze-update

Running Your First Review

  1. Ensure your SRS files are saved and included in org-srs-files or org-agenda-files.
  2. M-x org-srs-review-start
  3. Org-srs opens the first item, narrows to the question and hides the answer.
  4. Rate your recall:
    • e → Easy
    • g → Good
    • h → Hard
    • a → Again
  5. Org-srs reveals the answer, updates the schedule, and proceeds to the next item.
  6. At session end, you see a summary of items reviewed and next review dates.

First Review Workflow

  1. Open Emacs and switch to an Org buffer in ~/org/srs/.
  2. M-x org-srs-review-start
  3. Answer each prompt using the keys above.
  4. After the session, check your srs-data.el for updated intervals and next-due dates.

You’re now set to build and review your knowledge base entirely within Org-mode. Happy learning!

User Guide

This guide covers common workflows in Org-srs: authoring items, embedding and prettifying content, running review sessions, logging progress, querying items, and visualizing statistics.

1. Authoring SRS Items

1.1 Creating Flashcard Items

Require the card module and insert a new card under an Org heading:

(require 'org-srs-item-card)

;; Interactive: prompt for front/back
M-x org-srs-item-card-create RET

;; Programmatic example:
(org-srs-item-card-create
 :front '(plain . "What is the capital of France?")
 :back  '(plain . "Paris")
 :properties '((Category . "Geography")))

This creates an Org subtree with SRS metadata and card content.

1.2 Creating Cloze Deletions

Use the DWIM command to turn text into a cloze:

(require 'org-srs-item-cloze)

;; Cloze an active region
(call-interactively #'org-srs-item-cloze-dwim)

;; Cloze the word at point
(call-interactively #'org-srs-item-cloze-dwim)

;; Batch-cloze in a table
(let ((org-srs-item-cloze-table-range "@1$1..@>$2"))
  (call-interactively #'org-srs-item-cloze-dwim))

Customize hints and ID generation:

(setq org-srs-item-cloze-hint    "synonym")
(setq org-srs-item-cloze-identifier
      #'org-srs-item-cloze-identifier-number-sequence)

After edits, update cloze metadata:

M-x org-srs-item-cloze-update RET
;; Prefix argument forces full re-ID

2. Embedding and Prettifying

2.1 Overlay Rendering

Enable non-intrusive overlays to hide raw markers:

(require 'org-srs-embed)
(add-hook 'org-mode-hook #'org-srs-embed-overlay-mode)

;; Toggle manually
M-x org-srs-embed-overlay-mode RET

2.2 Embedding Cloze Markers

Insert raw markers around a region:

;; Interactive DWIM wrapper
M-x org-srs-embed-cloze-dwim RET

;; Programmatic:
(org-srs-embed-cloze (point-min) (point-max) "hint")

2.3 Removing Markers

Strip all embedded markers and restore plain text:

;; On the current buffer
(org-srs-embed-uncloze (point-min) (point-max))

3. Conducting Review Sessions

3.1 Starting a Session

Require the review module and launch:

(require 'org-srs-review)

;; Default: reviews due items in current buffer
M-x org-srs-review-start RET

;; Programmatic with custom source & strategy
(org-srs-review-start
 :source   (org-srs-review-source-dwim)
 :strategy (org-srs-review-strategy-due-first))

3.2 Navigation & Ratings

During a session:

  • n / SPC: next item
  • p : previous
  • q : quit session
  • 1 (again), 2 (hard), 3 (good), 4 (easy): rate current item

For mouse/touch support:

(require 'org-srs-mouse)
(org-srs-mouse-mode 1)

4. Logging Progress

4.1 Inserting a New Log Entry

Record initial scheduling parameters:

;; Place point in an SRSITEMS drawer
M-x org-srs-log-insert RET

;; Or programmatically
(org-srs-log-insert)

4.2 Updating an Existing Entry

After reviewing, update algorithm fields:

;; Default quality = last rating
(org-srs-log-repeat)

;; Adjust quality
(org-srs-log-repeat :quality 4)

;; Force timestamp to now
(org-srs-log-repeat :timestampp t)

;; Supply explicit timestamp
(org-srs-log-repeat :timestamp "2025-08-06T09:00:00Z" :quality 5)

4.3 Key Bindings Example

Bind logging commands in review mode:

(define-key org-srs-review-mode-map (kbd "i") #'org-srs-log-insert)
(define-key org-srs-review-mode-map (kbd "r") #'org-srs-log-repeat)

5. Querying Review Items

5.1 Building Predicates

Use constructors for common filters:

(require 'org-srs-query)

;; Never reviewed
(setq p-new    (org-srs-query-predicate-new))
;; Due now
(setq p-due    (org-srs-query-predicate-due))
;; Learned today
(setq p-learn  (org-srs-query-predicate-learned))

;; Combine: due and not new
(setq p-combo (org-srs-query-predicate-and p-due (org-srs-query-predicate-not p-new)))

5.2 Running Queries

Collect matching items:

;; In current buffer
(org-srs-query p-combo)

;; In a file
(org-srs-query p-combo "/path/to/notes.org")

;; Across a directory
(org-srs-query p-combo "~/org/")

6. Analyzing Progress

6.1 Review Counts Chart

Show daily review counts as a bar chart:

(require 'org-srs-stats-history)

M-x org-srs-stats-history-reviews RET

;; Lisp:
(org-srs-stats-history-reviews (org-srs-review-source-dwim))

6.2 Distinct Items Chart

Plot unique items reviewed per day:

M-x org-srs-stats-history-review-items RET

6.3 Retention Rates Chart

Visualize retention (no “again” responses):

M-x org-srs-stats-history-retentions RET

6.4 Custom Time Span

Fetch raw history and chart manually:

(cl-multiple-value-bind (dates records)
    (org-srs-stats-history (org-srs-review-source-dwim) 30)
  (chart-bar-quickie
   'vertical "30-Day Reviews" dates "Date"
   (mapcar #'length records) "Reviews"))

7. Calculating Review Intervals

Compute next-intervals for ratings:

(require 'org-srs-stats-interval)

;; Single rating
(org-srs-stats-interval :good)

;; Multiple ratings
(org-srs-stats-intervals '(:again :good :easy))

Use these functions to predict scheduling or integrate into custom commands.

Configuration

Org-srs offers multiple layers of tunable options—global variables, inheritable properties, scheduling tweaks, algorithm parameters, and UI adjustments—so you can tailor your spaced-repetition workflow.

Defining Org-srs Custom Properties

You can declare new Org-srs options that support per-directory, per-buffer, per-heading or per-item overrides via #+ATTR_SRS:. Use org-srs-property-defcustom to define both a customizable variable and a context-aware accessor.

Macro Signature

(org-srs-property-defcustom NAME
  &rest DEFCUSTOM-ARGS)
  • NAME: symbol, must start with org-srs-
  • DEFCUSTOM-ARGS: same as defcustom, plus:
    • :transform – function to post-process the raw value (defaults to identity)
    • :inherit – inheritance behavior for Org :PROPERTY: overrides (defaults to t)

Under the hood, this:

  1. Calls defcustom with NAME and your args.
  2. Defines an accessor function NAME that reads:
    • Overrides from #+ATTR_SRS: at point
    • An org-entry-get of the uppercase property
    • The global default

Example

Declare a “card interval” option (in days):

(org-srs-property-defcustom org-srs-card-interval
  :type 'integer
  :group 'org-srs
  :safe 'integerp
  :default 3
  :transform #’identity
  :inherit t
  :documentation
  "Default interval (in days) for newly created SRS cards.")

This generates:

  • A customizable variable (M-x customize-group RET org-srs RET)
  • An accessor function (org-srs-card-interval)

Using the Accessor

  1. Global default
    Set via Customize or (setq org-srs-card-interval 5).

  2. Heading/item override

    * French vocabulary
      #+ATTR_SRS: :card-interval 7
    

    (org-srs-card-interval) returns 7 within that heading.

  3. Temporary override in code

    (org-srs-card-interval 10
      (lambda ()
        ;; Inside this lambda, the accessor returns 10
        (message "Interval is %d" (org-srs-card-interval))))
    

    For multiple bindings, use:

    (org-srs-property-let ((org-srs-card-interval 14)
                           (org-srs-another-option   42))
      (schedule-new-cards))
    

Best Practices

  • Name properties semantically: org-srs-<thing>-<unit>.
  • Document each option with :documentation.
  • Use heading-level overrides for fine-grained control.
  • Set :inherit nil to prevent cascading.

Scheduling Tweaks

Burying Mechanism

Postpone sibling items to avoid reviewing related cards on the same day.

Configuration

;; Enable sibling burying
(setq org-srs-schedule-bury-sibling-items-p t)

;; Postpone interval (default 1 day)
(setq org-srs-schedule-bury-interval '(2 :day))

Hook setup:

(add-hook 'org-srs-review-after-rate-hook
          #’org-srs-schedule-bury-update-due-timestamp 40)

Fuzzing Mechanism

Add randomness to due dates to spread review load.

Configuration

(setq org-srs-schedule-fuzz-ranges
      '(((1 :day) . 0.2)
        ((7 :day) . 0.1)
        ((30 :day). 0.05)))

(setq org-srs-schedule-fuzz-unit '(6 :hour))

;; Custom interval function
(defun my-fuzz-interval (interval &optional unit)
  (let ((s (org-srs-time-desc-seconds interval))
        (u (org-srs-time-desc-seconds unit)))
    (list (+ s (/ u 2)) :sec)))
(setq org-srs-schedule-fuzz-interval #’my-fuzz-interval)

Hook setup:

(add-hook 'org-srs-review-after-rate-hook
          #’org-srs-schedule-fuzz-update-due-timestamp 60)

Offsetting Mechanism

Preserve context when learning ahead or reviewing overdue items.

Configuration

;; Always apply learn-ahead offset
(setq org-srs-schedule-offset-learn-ahead-time-p t)

;; Or only when due “today”
(setq org-srs-schedule-offset-learn-ahead-time-p #’org-srs-time-today-p)

Hooks:

(add-hook 'org-srs-review-before-rate-hook
          #’org-srs-schedule-offset-save-due-timestamp)
(add-hook 'org-srs-review-after-rate-hook
          #’org-srs-schedule-offset-update-due-timestamp 70)

Stepped (Re)learning Mechanism

Emulate Anki’s learning steps for new or failed cards.

Configuration

(setq org-srs-schedule-step-learning-steps '((1 :minute) (10 :minute) (1 :hour)))
(setq org-srs-schedule-step-relearning-steps '((5 :minute) (30 :minute)))

Hook setup:

(add-hook 'org-srs-review-after-rate-hook
          #’org-srs-schedule-step-update-due-timestamp 50)

Practical usage:

  • Use short units (seconds/minutes) for intensive initial repetition.
  • Rebind these variables dynamically for decks or tags via org-srs-property-let.

FSRS Parameter Optimization

Automatically tune FSRS scheduler parameters using past review history.

Workflow

  1. Export review logs to CSV
  2. Run Python fsrs-optimizer
  3. Apply returned parameters as file- or directory-local variables

Key Functions

org-srs-algorithm-fsrs-optimizer-insert-review-log
org-srs-algorithm-fsrs-optimizer-start-process
org-srs-algorithm-fsrs-optimizer-optimize
• Interactive org-srs-algorithm-fsrs-optimize

Code Examples

Programmatic optimization:

(require 'org-srs-algorithm-fsrs-optimizer)

(org-srs-algorithm-fsrs-optimizer-optimize
 markers
 (lambda (&key weights request-retention maximum-interval &allow-other-keys)
   (message "New weights: %S" weights)
   (message "Retention target: %.2f" request-retention)
   (message "Max interval: %S days" maximum-interval)))

Interactive optimization (M-x org-srs-algorithm-fsrs-optimize):

  • No prefix: current file or Dired directory
  • C-u: prompt for file or directory

After completion, Emacs inserts:

#+PROPERTY: SRS_ALGORITHM (fsrs <weights> <request-retention> <max-interval>)

Tips

  • Install Python package fsrs-optimizer (≥6.0).
  • Ensure entries have past review logs.
  • Customize day-start hour or timezone; Emacs passes these automatically.
  • After insertion, use C-c C-c to apply local variable changes.

Child Frame Utilities

Provide an API to create, reuse, and manage undecorated “child” frames for pop-ups or overlays.

Core Functions

org-srs-child-frame
org-srs-child-frame-p
org-srs-child-frame-root
org-srs-child-frames / (setf org-srs-child-frames)

Code Examples

Create and show a bottom panel at 30% parent height:

(let ((panel (org-srs-child-frame
              'preview-panel
              :size   0.3
              :position :bottom
              :buffer (get-buffer-create "*preview*"))))
  (with-selected-frame panel
    (erase-buffer)
    (insert "Hello from child frame!")
    (set-frame-visible panel t)))

Hide or delete a panel:

;; Hide if visible
(let ((panel (org-srs-child-frame 'preview-panel)))
  (when (frame-visible-p panel)
    (make-frame-invisible panel)))

;; Delete all ‘preview-panel’ frames
(setf (org-srs-child-frames 'preview-panel t) nil)

List all child frames:

(org-srs-child-frames)             ; all frames
(org-srs-child-frames 'preview-panel t)  ; only ‘preview-panel’

Practical Guidance

  1. Always use org-srs-child-frame for consistent styling and reuse.
  2. Control visibility with set-frame-visible / make-frame-invisible.
  3. Position via :position—either :bottom or (X . Y) absolute pixels.
  4. Clean up with (setf (org-srs-child-frames NAME t) nil).

Developer Guide

Extend Org-srs by plugging in custom algorithms, query predicates, or review strategies. Each subsection below shows how to hook into the core interfaces and register your own implementations.

Implementing a Custom Spaced-Repetition Algorithm

Define two methods in org-srs-algorithm.el:

  1. org-srs-algorithm-ensure normalizes your config into state
  2. org-srs-algorithm-repeat advances that state on each review

1. Configure your algorithm

In your init.el:

(setq org-srs-algorithm
      '(my-custom-algo :initial-interval 1 :factor 2.5))

2. Define ensure

(require 'org-srs-algorithm)
(cl-defmethod org-srs-algorithm-ensure
    ((algo (eql my-custom-algo)) &rest args)
  "Initialize STATE plist for ‘my-custom-algo’."
  (let ((plist (apply #'list :algo algo args)))
    ;; set defaults
    (plist-put plist :interval (or (plist-get plist :initial-interval) 1))
    (plist-put plist :factor   (or (plist-get plist :factor) 2.0))
    plist))

3. Define repeat

(cl-defmethod org-srs-algorithm-repeat
    ((state plist))
  "Advance STATE by multiplying :interval by :factor."
  (let* ((interval (plist-get state :interval))
         (factor   (plist-get state :factor))
         (new-int  (max 1 (round (* interval factor)))))
    (plist-put state :interval new-int)))

4. Test interactively

;; Initialize
(setq my-state (org-srs-algorithm-ensure 'my-custom-algo :initial-interval 1 :factor 2))
;; Advance
(org-srs-algorithm-repeat my-state)
;; => (:algo my-custom-algo :initial-interval 1 :factor 2 :interval 2)

Org-srs will now call your ensure and repeat on each review cycle and save the returned state into the entry’s properties.


Creating Custom Query Predicates

Use org-srs-query-predicate in org-srs-query.el to build filters, then pass them to any query function.

Predefined forms

• Symbol: 'due, 'new, 'reviewed
• List: '(and due (not reviewed))
• Function: any zero-arg predicate returning non-nil on match

Combining predicates

;; due and not suspended
(let ((pred (org-srs-query-predicate '(and due (not suspended)))))
  (org-srs-query-buffer pred (current-buffer)))

Custom predicate

(defun my-priority-p (min-pri)
  "Return a predicate that matches items with priority > MIN-PRI."
  (lambda ()
    (> (org-srs-item-get-property org-srs-item-data :priority)
       min-pri)))

;; Use in a query
(org-srs-query-directory
 '(and due (my-priority-p 10))
 "~/org/flashcards/")

All query entry points accept the same forms

(org-srs-query-region '(or new due)
                      (point-min) (point-max))
(org-srs-query-file '(reviewed) "/path/to/file.org")
(org-srs-query '(and due (not reviewed)))

Defining New Review Strategies

Review strategies live in org-srs-review-strategy.el. You register a symbol and provide a method that returns a list of items.

1. Register a strategy symbol

Set your strategy name:

(setq org-srs-review-strategy 'due-priority-first)

2. Define the strategy method

(require 'org-srs-review-strategy)
(cl-defmethod org-srs-review-strategy
    ((strategy (eql due-priority-first)))
  "Select due items, sorted by descending priority."
  (let ((items (org-srs-query '(due))))
    ;; org-srs-item-priority returns a number
    (cl-sort items #'> :key #'org-srs-item-priority)))

3. Run your strategy

;; Start review using your custom strategy
(org-srs-review-start)

The review engine calls your org-srs-review-strategy method, presents each item, and updates its state via the configured algorithm.


Practical tips
• Name each symbol uniquely to avoid conflicts.
• Reuse org-srs-query inside strategies for complex filters.
• Sort with cl-sort and item properties (e.g. :due, :priority).
• Test with M-: (org-srs-review-strategy 'your-symbol) before full review.

API Reference

Alphabetical list of interactive commands, public functions, variables, and customization groups for quick look-ups.

Interactive Commands

  • org-srs-add-item
    (interactive)
    Add the current Org heading as a new SRS item.

  • org-srs-backlog-review
    (interactive &optional days)
    Review items overdue by more than DAYS (default: 0).

  • org-srs-delete-item
    (interactive ITEM-ID)
    Remove the item identified by ITEM-ID from the SRS database.

  • org-srs-review
    (interactive &optional count)
    Conduct a review session for up to COUNT due items (default: all).

  • org-srs-show-stats
    (interactive &optional period)
    Display statistics over the last PERIOD (days, weeks, or months).

Public Functions

  • org-srs-calc-next-interval
    (difficulty rating &optional algorithm) → integer
    Compute the next review interval (in days) for a given DIFFICULTY rating (0–5) using ALGORITHM (default: org-srs-default-algo).

  • org-srs-deserialize-data
    (&optional file) → plist
    Load and return the SRS database from FILE (default: org-srs-db-file).

  • org-srs-get-due-items
    (&optional date) → list
    Return a list of items scheduled for review on DATE (default: today).

  • org-srs-log-review
    (item-id quality &optional timestamp)
    Record a review result for ITEM-ID with QUALITY (0–5) at TIMESTAMP (default: now).

  • org-srs-schedule-items
    (items &optional start-date)
    Assign review dates to ITEMS beginning from START-DATE (default: today).

  • org-srs-serialize-data
    (db-data &optional file)
    Write DB-DATA (plist) to FILE (default: org-srs-db-file).

  • org-srs-update-item
    (item-id &rest properties)
    Modify PROPERTIES of the item identified by ITEM-ID.

Variables

  • org-srs-db-file (string)
    Path to the file storing serialized SRS data.

  • org-srs-default-algo (symbol)
    Scheduling algorithm to use by default. Options: fsrs, sm2.

  • org-srs-review-buffer (string)
    Name of the temporary buffer for review sessions.

  • org-srs-timezone (string)
    Timezone identifier used for all date calculations.

Customization Groups

  • org-srs
    Core settings for the Org SRS system.

  • org-srs-algorithms
    Parameters for scheduling algorithms (FSRS, SM2).

  • org-srs-display
    Appearance and layout options for review buffers.

  • org-srs-logging
    Controls logging format, file locations, and verbosity.