The Rust Engine Behind FretsNotes


Every exercise in FretsNotes — every note prompt, every mastery score, every adaptive difficulty adjustment — runs through a single Rust engine. We call it NotesKit. Here’s why we built it this way and what it gives us.

Why Rust?

We needed a few things from the engine layer:

  1. Correctness — Music theory has rules. A note is either right or wrong. The engine needs to be deterministic and testable.
  2. Performance — Mastery-weighted scheduling involves sorting and sampling across hundreds of prompts. It needs to be fast, even on older devices.
  3. Portability — We wanted one codebase for iOS, Android, and eventually web (via WASM).

Rust gives us all three. Strong typing catches entire categories of bugs at compile time. Performance is native-speed without a garbage collector. And the ecosystem has mature tools for cross-compilation.

UniFFI: One Engine, Three Platforms

Mozilla’s UniFFI generates language bindings from Rust to Swift and Kotlin. We define our types and functions once in Rust, and UniFFI produces the Swift Package and Kotlin library automatically. The engine compiles to a native .dylib on iOS and a .so on Android.

This means when we add a new exercise mode or fix a scheduling edge case, it ships to both platforms simultaneously. Zero divergence.

What the Engine Does

NotesKit handles the heavy lifting:

  • Exercise specification — Defines the 16 exercise modes with their parameters (strings, frets, pitch classes, spelling policy)
  • Prompt generation — Creates prompts based on the exercise spec, respecting no-repeat rules and ordering policies
  • Mastery-weighted scheduling — Tracks per-note mastery scores and prioritizes weaker areas
  • Adaptive difficulty — Four-stage promotion and demotion: octave expansion, chromatic inclusion, wider fret range, time pressure
  • Hint system — Progressive level 1-2-3 hints for all 16 exercise modes
  • Curriculum parsing — Loads course JSON files, validates prerequisites, manages the course graph

307 Tests, Deterministic RNG

The engine has 307 tests covering unit logic, integration scenarios, and roundtrip validation. We use a seeded SplitMix64 random number generator so that given the same seed and mastery state, the engine produces identical prompt sequences. This makes bugs reproducible and testing reliable.

What’s Next

We’re working on the WASM build for the web app. Same engine, same tests, running in the browser. When it ships, you’ll be able to practice on any device with a web browser — no install required.