Problem Statement
I revise by explaining things out loud to nobody. It works, because explaining forces you to find the holes. The problem is nobody is listening, so nothing tells you where the holes were.
Every AI study tool I have used has the same flaw. It already knows the material. So when my explanation is vague, it fills the gap for me and I never find out I was vague. I finish the session feeling like I understood it, with no evidence either way.
Solution Overview
Star Pupil gives you a student instead. Pip knows nothing about your topic and believes exactly what you tell him, including the things you explain badly.
When you enroll, before you type anything, an examiner writes a map of the subject and a six question exam sealed against it. You teach by typing. Every sentence becomes a belief in Pip's head, right or wrong. Then he sits that sealed paper alone and answers only from what you taught him.
He fails, usually. The report card shows you why, and every lost mark points at a sentence you wrote.
Key Features
Everything is a view onto one structure. After each message, an extraction pass writes down what a real student would now believe. Not what is true. What your wording licenses.
interface Belief {
statement: string; // what Pip now believes
status: "correct" | "wrong" | "fuzzy";
quote: string; // your exact words that produced it
derivedFrom: number[]; // beliefs this was reasoned from
correction?: string; // what you could have said instead
}
| Feature | What it does |
|---|---|
| Live misconceptions | Say something sloppy and the concept turns red while you watch, quoting your sentence as the source |
| Sealed paper | Syllabus, questions and mark scheme fingerprinted with SHA-256 in your browser at enrollment, recomputed on the report card |
| Frontier map | You only see what you could teach next. Show the full syllabus and the lesson becomes a checklist |
| Dispute | Tell Pip he never heard you say that. He reads your sentence back and keeps his note unless the quote really does not support it |
| Blank enforcement | If Pip says the lesson never covered something, the mark is zero even when his guess is right |
| Root tracing | A lost mark walks the derivation chain back to the first wrong belief, not the nearest one |
| Two scores | Coverage and accuracy separately. A confident 6/6 on half the topic is a different problem from a shaky 6/6 on all of it |
Technologies Used
Next.js 16, TypeScript, Tailwind, shadcn/ui. The map is React Flow over a dagre layout. The exam streams as NDJSON so the stages tick live. Web Crypto computes the seal in the browser. Canvas draws the downloadable report card, with no image export library.
The model layer is a ladder ordered by free tier quota rather than model quality, because the project runs on $0:
| Rung | Free quota | Why here |
|---|---|---|
| Gemini 3.1 Flash Lite | 500/day | Leads. Enforces a response schema |
| Groq gpt-oss-120b | 1,000/day | Separate bucket, real depth |
| Groq llama-3.3-70b | 1,000/day | Separate bucket again |
| Gemini 3 Flash | 20/day | Best model here, so it goes last |
Putting the best model first would be wrong. On a free key it is almost always the rung that is already spent. Every (model, key) pair is its own step, so a 429 rotates keys before dropping to a weaker model. Calls are bounded twice, 8 seconds per attempt and 45 seconds per ladder, because Vercel kills the function at 60 and returns an HTML page that the frontend cannot parse.
No accounts, no server state, no database. The lesson lives in your browser.
Target Users
Students who already revise by explaining out loud and want to know where the holes are. Anyone rehearsing a presentation against a listener who cannot fill in their gaps. Tutors who want to show a student the difference between a confident explanation and a correct one.
Challenges I ran into
The exam used to be written after the lesson, and it was fed the concepts that came out shaky. It demoed beautifully. The exam always found the trap I planted.
Then I watched someone else use it and they said, out loud, "well of course it caught that, it read what I taught."
They were right. An exam written after the lesson, aimed at the lesson's weak spots, is a mirror. Every wrong answer I had been proud of was unfalsifiable.
So I moved the paper to enrollment and sealed it so you can check it did not move. This cost me the good demo. The exam now asks about things you never touched, and it does that often. It turned out to be the better product, because those questions produce Pip's confessions, and that is where coverage as a separate score came from.
What I learned
A prompt is not a guarantee.
I asked the grader to mark confessed gaps as blank, and it mostly did. Then I ran the same exam across different models. One of them confessed the gap, then guessed the right answer out of its own knowledge, and the grader handed it the mark. The score had stopped measuring my teaching and started measuring what the model already knew.
The fix is four lines and it runs after the model has spoken:
const confessed = answers[i]?.confessed && !answers[i]?.usedBeliefIds?.length;
if (confessed && g.verdict !== "blank") {
return { verdict: "blank", graderVerdict: g.verdict, culpritBeliefId: null };
}
The guess still prints on the report card, greyed out, worth nothing. Rules you actually need get enforced in code, after the model has spoken, or they are not rules.
Built With
- dagre
- gemini
- groq
- next.js
- react-flow
- shadcn-ui
- tailwind
- typescript
- vercel
- web-crypto
Log in or sign up for Devpost to join the conversation.