Replacing Anki With org-drill

Posted on Fri, 13 Jul 2018 at 09:53:13 EST

Tagged as writeup, emacs, practices, lisp, and math.

Recently, I read Michael Nielsen's essay, "Augmenting Cognition". It talks about some very interesting use cases for the spaced repetition software "Anki" that made me want to try it out again. I'm familiar with Anki, as I used it extensively throughout my last year of high school to study for AP exams. At the time, Anki's "killer feature" for me over similar software was being able to typeset mathematical notation in LaTeX (the exams were Chemistry and Calculus, so almost all of the material to memorize was mathematical notation). It's a great piece of software; I've been using it with the brother I'm helping through summer school. But ever since I began using Gentoo, I've been trying to avoid packages like QtWebView, which has deterred me from installing Anki on my machine. With a little bit of searching, however, I found that there was an Emacs package for spaced repetition named 'org-drill', so I decided to check it out.

org-drill is included in org by default (which happens to be included in Emacs by default), but it does need to be enabled. The steps to do so are outlined on the corresponding worg page. So far, I've used it to study German vocabulary and the material for my ham radio license exams, and I'm very happy with it. It has all of the features you might want from Anki, like Cloze deletion and double-sided cards, but I find that card creation is even more intuitive in org markup. Clozes are as simple as enclosing the answers in square brackets, and multi-sided cards just entail making multiple headings and setting the ":DRILL_CARD_TYPE:". You can even write your own card types in elisp. Another benefit of using org markup as the source for cards is that I can easily transform a plain text file into a deck using emacs macros.

Unlike Anki, however, org-drill has support for the SM5 and SM8 scheduling algorithms. Anki is quite outspoken about the benefits of SM2 over the later renditions, but I appreciate that I at least have the option to use these schedulers if I want to. The algorithms' parameters can also be finely tuned; the one I've found most useful is 'org-drill-learn-fraction', which I can use to decrease the amount of time before I see a card again.

As I mentioned earlier, the feature that brought me to Anki was its support for typesetting math with LaTeX. Emacs certainly has support for rendering LaTeX, but I have a pretty wonky setup where I'm running Emacs in a terminal emulator, so what I opted for instead was a typesetting language that renders to unicode text. There are quite a few of these, but the one I was most impressed with is Diagon. It's meant to be run in the browser, but the backend is written in C++ and can be compiled to run natively. Be warned, however, that the build system does require Java.

First, I replace 'src/main.cpp' with the following. The version in VCS will unconditionally run the SequenceTranslator, but this modification enables us to select which translator to use from a command-line argument.

#include "translator/Translator.h"
#include <iostream>

int main(int argc, const char **argv) {
    if (argc != 2) {
        std::cerr << "usage: " << argv[0] << " [translator]" << std::endl;
        return 1;

    std::string input;
    for (std::string line; std::getline(std::cin, line);) {
        input += line + "\n";

    auto translator = TranslatorFromName(argv[1]);
    std::cout << (*translator)(input, "") << std::endl;

    return 0;

Then, compiling is as easy as

cd tools/antlr/
cd ../../
mkdir build
cd build
cmake ..

And for Emacs integration, I've added the following to my '.emacs'

;; Applies Diagon's "Math" formatter to the current region, replacing
;; the contents of the region with the formatted output.
(defun format-math-at-region ()
  (let* ((math-to-format (buffer-substring (region-beginning) (region-end)))
         (command (format "echo \"%s\" | diagon Math" math-to-format))) ;; Bad and hacky. I'm aware.
      (kill-region (region-beginning) (region-end))
      (insert (string-trim-right (shell-command-to-string command)))))

It's not as powerful as LaTeX, but it certainly suits my needs.