matthorn.io

A lesson engine in plain files

· Matt Horn

A lesson is two plain files: lesson.md holds the prose and the starter code, grade.py holds the checks. It is complete when the checks pass. The editor is a local web app that runs Python as WebAssembly (Pyodide) in the browser. Progress is one JSON file in your home directory.

I built it because I wanted to re-learn ordinary differential equations in SciPy and could not find a tutorial for that combination. Nothing about that belongs on a hosted course platform.

The five types differ in how much of the answer is already in front of you:

TypeYou
read_runread a worked example and run it
explorevary a working program and observe what changes
debugfix a broken program
completefill in the missing part of a partial solution
writewrite the solution from scratch

Two effects shaped that order. Retrieval beats rereading, though only at a delay: on an immediate test it loses. And a studied solution beats an unaided attempt for someone new to the material, an advantage that reverses once they already know it. What reconciles the two is fading, which is the thing the sequence is actually doing: take the worked example away one step at a time.

The engine vendors everything at install time, Pyodide included, so once installed it works on a plane and behind a firewall. One feature sends anything off the machine: an AI check per lesson, off by default, which posts your code and the lesson prompt to a model.

The code is on GitHub: lesson-engine. If you run a lesson, tell me what broke.

All posts · Home