Skip to content

zsh-eng/spaced

Repository files navigation

🔭 spaced

Check out spaced here!

🔭 spaced is a modern flashcard app that helps you learn faster and remember longer. It uses spaced repetition to help you learn more efficiently.

Note: This is project is still in early development. If you have any suggestions or feedback, please feel free to open an issue.

Motivation

Spaced repetition is proven to be one of the most effective ways to learn.

Currently, there are many great apps out there that use spaced repetition, such as Anki, SuperMemo, and RemNote.

Personally, I use obsidian-spaced-repetition, which I love because of the tight integration with Obsidian. I believe that markdown is the best way to store your flashcards.

However, one aspect that I dislike about both obsidian-spaced-repetition (and other tools like ObsidianToAnki) is the separator syntax that is used to create flashcards. For my own personal workflow, I use Obsidian to write notes , but writing flashcards adds clutter. Fundamentally, I believe that flashcards should be treated as separate entities that are colocated with your notes, but different in the following ways:

  1. Flashcards should only be seen during review.
  2. There is no need to search / index / tag flashcards in the same way that notes are searched. The whole point of flashcards is to only appear when they need to be reviewed.

Thus, I created spaced, which focuses on handling the spaced repetition aspect of learning. This way, I can focus on writing notes that are easy to read and understand (in Obsidian), and create flashcards that are effective for spaced repetition (in spaced).

Example

Here's an example of my existing notes using Obsidian Spaced Repetition:

#### Dynamic and Static Binding

What is dynamic binding (aka late binding)?
?
A mechanism where _method calls_ in code are resolved at **runtime** rather than at compile time.

<!--SR:!2024-01-02,25,249-->

What is static binding?
?
When a _method call_ is resolved at _compile_ time.

<!--SR:!2024-03-24,94,271-->

Overriden and overloaded methods: static or dynamic binding?
?
Overridden methods are resolved using **dynamic binding**, and therefore resolves to the implementation in the actual type of the object.
In contrast, overloaded methods are resolved using **static binding**.

<!--SR:!2024-03-12,85,271-->

Here's my ideal syntax:

#### Dynamic and Static Binding

**Dynamic Binding (Late Binding)**: A mechanism where _method calls_ in code are resolved at **runtime** rather than at compile time.
Overriden methods are resolved using **dynamic binding**.

<!-- id:<some-card-id-here, where flashcards are stored separately> -->

**Static binding**: When a _method call_ is resolved at _compile_ time.
Overloaded methods are resolved using **static binding**.

<!-- id:<some-card-id-here, where flashcards are stored separately> -->

Why not just use local storage? Why not keep it all in Obsidian?

  1. I don't always have access to my Obsidian vault - I want to be able to review flashcards on the go.
  2. I want to be able to easily share flashcards with others.
  3. I want to create a UI/UX that I personally enjoy using. One of the big criticisms that I have of obsidian-spaced-repetition is that it doesn't store the history of your reviews. You also cannot undo a review, which is annoying if you accidentally mark something as "easy".

Self Hosting

If you want to self-host this project, you can do so by following these steps:

  1. Fork the repository.

  2. Generate a new AUTH_SECRET with the following command

    pnpm dlx auth secret
  3. Create a new GitHub OAuth App.

  4. Fill in the .env file with the AUTH_GITHUB_ID and AUTH_GITHUB_SECRET.

  5. Create a new database in Turso.

  6. Fill in the .env file with the TURSO_DATABASE_URL and TURSO_AUTH_TOKEN.

  7. (Optional): To support image uploads, I use a custom Cloudflare Worker. If you don't want to, just fill in a dummy value for CLOUDFLARE_IMAGE_UPLOAD_WORKER_TOKEN.

  8. Run the following command to create the database tables:

    pnpm db:push
  9. Deploy the app to Vercel.

  10. Done! You should now be able to access your own instance of spaced.

Resources

Acknowledgements

This project was built using the following tech stack:

  • Next.js
  • UI: TailwindCSS, shadcn/ui
  • Backend: tRPC
  • Database: Turso
  • ORM: Drizzle

Great Libraries:

I've also learned a lot from the following resources:

Finally, a special thanks to @ishiko732 for answering questions regarding ts-fsrs and spaced repetition.