A Go library for building spaced-repetition systems with FSRS.
go get github.com/open-spaced-repetition/go-fsrs/v4@latestpackage main
import (
"fmt"
"time"
"github.com/open-spaced-repetition/go-fsrs/v4"
)
func main() {
s := fsrs.NewFSRS(fsrs.DefaultParam())
now := time.Now()
card := fsrs.NewCard(now)
// Preview all four ratings, then apply one.
preview, err := s.Repeat(card, now)
if err != nil {
panic(err)
}
fmt.Println(preview[fsrs.Good])
review, err := s.Next(card, now, fsrs.Good)
if err != nil {
panic(err)
}
card = review.Card
}See the GoDoc for the full API reference.
*FSRS is the complete scheduling layer. It walks a Card through its lifecycle
(New → Learning → Review → Relearning), applies learning steps and short-term
memory updates, optionally fuzzes intervals, and validates all input with
structured errors. This is the recommended entry point for most applications:
| Method | Returns | Purpose |
|---|---|---|
Repeat(card, now) |
(RecordLog, error) |
Returns the result of each potential rating. |
Next(card, now, grade) |
(SchedulingInfo, error) |
Reviews the card with a given rating; returns the updated card + ReviewLog. |
Retrievability(card, now) |
(float64, error) |
Current probability of recall. |
Reschedule(card, reviews, opts) |
(RescheduleResult, error) |
Replay a review history; rebuild state and due date. |
Forget(card, now, resetCount) |
(SchedulingInfo, error) |
Reset a card to New, preserving its ReviewLog. |
Rollback(card, log) |
(Card, error) |
Revert a review using its ReviewLog. |
MemoryState(history, start) |
(*MemoryState, error) |
Derive stability/difficulty from ReviewEntries. |
HistoricalMemoryStates(history, start) |
([]MemoryState, error) |
All intermediate memory states. |
Parameters exposes the raw FSRS algorithm — given a MemoryState and a rating,
it computes the next stability, difficulty, and ideal review interval. There is no
scheduler, no card state machine, no learning steps, and no fuzz — just the core
math that the scheduler itself builds upon:
| Method | Returns |
|---|---|
ForgettingCurve(elapsedDays, stability) |
float64 |
NextState(state, retention, days, grade) |
ItemState |
NextStates(state, retention, days) |
NextStates |
ApplyFuzz(interval, elapsedDays, enable) |
float64 |
NewBasicScheduler / NewLongTermScheduler return a lightweight *Scheduler that
drives a single Card through steps and fuzz without validation. Neither Preview()
nor Review(grade) returns an error:
| Method | Returns |
|---|---|
Preview() |
RecordLog |
Review(grade) |
SchedulingInfo |
p := fsrs.DefaultParam()
next := p.NextState(&fsrs.MemoryState{Stability: 5.0, Difficulty: 5.0}, 0.9, 3, fsrs.Good)
fmt.Println(next.Interval)
now := time.Now()
card := fsrs.NewCard(now)
s := p.NewBasicScheduler(card, now)
fmt.Println(s.Review(fsrs.Good).Card.Due)Enable interval fuzz with p.EnableFuzz = true. This will cause the scheduler to spread reviews across nearby days. The randomness is seeded per-card for reproducibility.
FSRS v6 uses a 21-element weight vector (Weights = [21]float64). DefaultWeights() returns the built-in baseline; the helpers below convert from older formats:
| Helper | Input | Converts from |
|---|---|---|
ConvertV5Weights |
[19]float64 |
FSRS v5 |
ConvertV45Weights |
[17]float64 |
FSRS v4.5 |
MigrateWeights |
[]float64 |
Auto-detect (17/19/21) |
Invalid input returns a typed *fsrs.Error with a machine-readable Code,
compatible with errors.Is / errors.As:
var fsrsErr *fsrs.Error
if errors.As(err, &fsrsErr) {
fmt.Println(fsrsErr.Code) // e.g. fsrs.ErrCodeInvalidInput
}Pull requests are welcome. Suggestions, bug reports, and feature ideas belong in Issues.
For algorithm design discussions, please use the discussions page.