How to write your own spaced repetition algorithm
Last updated April 10, 2021
This page will help you learn how to write your own spaced repetition algorithm. You can use my Spaced Repetition Schedule Simulator page to test out your algorithm.
What is spaced repetition?
Spaced repetition is a technique for scheduling when you should study material that you want to commit to memory. Flashcard apps like Fresh Cards use a spaced repetition algorithm to optimize your learning so that you don’t have to study everything every day.
If you are studying a new language, you will need to memorize hundreds or thousands of new words, but it wouldn’t make sense to review hundreds of words every single day. At some point, your memory of a word is good enough and you don’t need to review it. However, given enough time without practice, your memory of the word would slowly disappear.
Here’s where a spaced repetition algorithm comes in. After you learn a piece of information for the first time, your memory of that information will gradually “decay” until eventually it’s forgotten. A spaced repetition algorithm attempts to model that decay and predict when you are likely to forget. It then computes the optimum time in the future to review so that you can “boost” your memory again. After that “boost”, your memory will be good again.
The good news is that each time you give your memory a boost, you strengthen it and the next review after that can be even further away in the future. Eventually, the periods between reviews would become so long that the fact would essentially become “permanent” in your memory.
What a spaced repetition algorithm does
Now that we know the purpose of a spaced repetition algorithm, let’s figure out how it does its thing.
I mentioned earlier that a spaced repetition algorithm attempts to model the decay of your memory. It does this by attempting to model how memorable a given fact is to you. This is super important.
No algorithm will be able to detect right off the bat whether a piece of information is memorable. Some facts are more memorable than others. For instance, memorizing “le chat” means “the cat” is probably much easier for you than memorizing 15 digits of pi. Secondly, no algorithm will be able to know right off the bat how good your particular memory is. Remembering 15 digits of pi might be easier for somebody else than you.
A good spaced repetition algorithm attempts to triangulate on “how memorable something is” and “how good your particular memory is” based on how well you recall the memories during reviews. Reviews are super important since it gives us information about both of these. For instance, if you struggle with memorizing 15 digits of pi, it should schedule more frequent reviews.
What information is available to triangulate
Okay, let’s talk about what information is available to help figure out how memorable something is and how good your memory is. Here’s a short list of information we can use to determine “memorability” of a fact:
- whether you were able to recall the fact or not during a review
- how long it took to remember the fact during a review
- how many times you were able to recall this fact in reviews correctly in a row
- how long the current interval since the last review is
- how late are you in reviewing this material versus when it was scheduled
These are the basic signals that tell us how memorable a fact is. In the next section I’m going to go into more specific details about how to write a function to model this.
Writing the srsFunc function
Let’s get into the details of how to write an algorithm in JavaScript using the Spaced Repetition Schedule Simulator. (Quick plug: whatever algorithm you write for the simulator page will work within Fresh Cards.) The simulator page has a text box where you can type out your algorithm. When you’re done editing and want to see the effect on the simulated schedules on the right, just hit the “Save edits” button.
You will need to know JavaScript to write the code for the simulator, and unfortunately I don’t have the time and space to teach it to you. I’ll assume you know how it works from here on out. I’m also going to assume that you will be using this function to test flashcard knowledge specifically, so the language will refer to flashcards.
The simulator page uses a function called srsFunc to represent your algorithm. Whenever you review an individual flashcard, this function will be used to compute when the next review should happen and any associated “metadata” we want to track to model the memorability of the specific card.
The function is called with two parameters: ‘previous’ and ‘evaluation’. previous represents the last known state of the model for the given card and evaluation represents information about the current review. The function returns an object that represents the new model of the memorability of the card.
Output
Let’s work backwards and talk about what the function needs to return before we talk about the inputs. It’ll be clearer this way since the output of the function call will be used as the input to the function the next time we review the card.
The output of the function must be an object with these properties:
- interval
- n
- efactor
- data (optional)
interval
This is the most property returned. It represents when to review this card again in the future. This is a floating point number that represents number of days. For instance, if you set this to 1.0, the next review will be in 24 hours. If you set this to 7.0, it will be in a week. Set it to 0.25 to review in 6 hours.
n
This represents how many times this card was reviewed successfully in a row. If you fail at remembering the card, this would get set to 0. If you remembered the card during the review, it would get incremented by 1 from the previous review.
efactor
This is the “e factor” and represents the ease of recall for this particular card. The concept of “e factor” comes from SuperMemo’s SM-2 algorithm, although you can redefine it for your purposes since your function fills this in. For interopability with other functions (assuming you are using Fresh Cards and you switch to this algorithm from Fresh Cards’ internal one), I recommend using these guidelines:
- the hardest cards should have an ease factor of 1.3, but no lower
- the easiest cards should have at most an ease factor of 2.5
- start with an ease factor of around 2.5 and adjust as necessary
data
Finally, the data property is optional. If you use it, you can set it to an object with whatever properties you choose. Otherwise, keep it undefined or null. This property is algorithm-specific, so don’t assume it will be filled in if you switch between algorithms.
Putting what we know together, here’s what the simplest algorithm would look like. It ignores the input parameters and just schedules a review 24 hours after the current one.
function srsFunc(previous, evaluation) {
return { n: 0, efactor: 2.5, interval: 1.0 }
}
Input parameters
Let’s talk about the input parameters.
previous is null on the first review, because we don’t have any metadata about the card yet. On subsequent reviews, it’s filled with the data from the last time we ran the srsFunc, i.e. it has these properties:
- interval
- n
- efactor
- data (optional)
Now let’s talk about the evaluation parameter. This represents the current review of the card. It only has two properties:
score
This is how well the card was recalled during the review.
lateness
This is a floating point number representing, in days, how late this review is. If a review is scheduled for Wednesday at 2pm and you review the card on Thursday at 2pm, this number will be 1.0. If you review it on Wednesday at 4pm (2 hours late), it would be 0.083 (2 hours x 1.0 days / 24.0 hours = 0.083).
Let’s talk about the score in more detail. The scoring system is based on 5-point scale and comes from SuperMemo’s SM-2 algorithm. A score of 5.0 is used for a review of material where you easily recalled the information. 3.0 represents a scenario where the material was recalled, but it was difficult. Anything below 3.0 represents a failure to remember. Think of 2.0 as “I couldn’t remember it, but when you showed me the answer, it sparked my memory” and 1.0 as “I couldn’t remember it, and when you showed me the answer, it was very foreign to me.”
Assuming you are using Fresh Cards, it will determine the score for a review based on a few factors: how quickly you looked at the answer after having seen the question, whether you swiped left or right, and, if applicable, how well you did on both sides of the card.
How to write the algorithm
Now that you know the inputs and outputs of the algorithm, you have enough to write your own. But how should you write it? This is where things get interesting. You have a lot of options available to you, but I recommend taking a look at the examples provided first. What they’re doing should be clearer now that you know the inputs and outputs they’re working with.
Remember, the goal of the algorithm is to take the efactor, n value, and last interval, along with the review info to compute the next efactor, n, and interval. Here are some “techniques” used by the example algorithms:
- The efactor feeds into the interval. The easier a card is, the longer the interval to review it should be.
- If a review is done late, but still correctly answered, that likely means the card was easier than we thought. Bump up the efactor and increase the interval more than usual.
- If a card was forgotten, but reviewed “on time”, then the efactor was likely too high and the interval was too long, reduce both.
- New cards typically have shorter intervals to allow you to practice them a lot early on. If you’re finding the reviews are too frequent, change the default efactor or initial interval. Likewise, if new cards are too hard to remember, try reduce the intervals.
- When you review several cards together for the first time, because of the default initial intervals, you may find you’re reviewing them all together in each lesson. This might lead to cross-associations between the words, which may make them easier to recall than they should be in isolation. To avoid this cross-association, try adding a random floating point number to the intervals to space them out.
I want to close out by mentioning that it’s important to remember that spaced repetition algorithms simply attempt to model your memory. They’re not going to be 100% accurate. That’s okay, for the most part because they are better than the alternative, which is trying to review everything all the time. Once you have an algorithm that works for you, it’s probably not worth it to hone it too much. In my opinion, at some point you are talking about differences in days or weeks to remember things. If you are just trying to study for an exam or learn a new language, you likely don’t need the absolute most optimal algorithm.
Where to learn more
Okay, this was a lot of info. I hope it was useful. If you have thoughts or questions, I have contact info on the Fresh Cards page. Or, check out the Discord for the app.
If you want to check out other algorithms and how they work, take a look at these links.
- Information about how Anki’s algorithm works - This doesn’t provide the actual code, although Anki is open source, so you could check it, or read the next link.
- How Anki computes intervals - This has a few mistakes in it, but it does provide some information on how the Anki code actually works.
- How the SM-2 algorithm works - Some pseudo-code on this page explains how SM-2 works, although even to a programmer like me, it was a little hard to understand at first.