The problem, stated plainly
Ask most study tools a question and you get paragraphs back.
For a student with ADHD, dyslexia, or a slower reading speed, that's the whole problem restated. The answer arrives in exactly the format that was already the barrier. You asked because reading was hard, and you were handed more reading.
Warm Start answers differently. It draws.
What it does
Type what you want to learn — "I don't get photosynthesis" is a perfectly good way to put it. There's one box and one button. No mode to pick, no settings to get through first, no account.
What comes back is a diagram on a whiteboard: the topic in the middle, its parts branching off, one short note beside each one saying the thing that matters. That's the whole explanation. The written version exists, but it's behind a button for whoever wants it.
Then two things happen that we think matter more than the first diagram:
It suggests what to ask next. Small tappable questions a curious student would naturally reach for — so the next step doesn't require knowing what to ask.
The board grows. Ask a follow-up and the new diagram is drawn beside the old one, joined by an arrow. Nothing is wiped. Your whole train of thought stays visible on one canvas, so you can see where you came from — which matters a lot if holding a thread is the thing you find hard.
It's a real whiteboard, so you can draw on it yourself with a pen tool.
What we built
The model can't write on the screen
Every model reply passes through a typed schema and a validator before anything renders. The model picks a shape and short labels; our code draws it.
- Labels are capped at 40 characters, so prose can't be smuggled onto the canvas
- Only five diagram shapes exist — anything else is rejected or coerced
- Every diagram carries a written description, so a screen reader gets the idea rather than the word "graphic"
Untidy gets repaired. Unusable gets refused.
This was the single biggest engineering lesson. Our first validator rejected any reply that broke a rule — a label three characters too long, a sixth branch, a missing description. Free models trip at least one of those most of the time, so perfectly good diagrams kept coming back as "a shape we couldn't use".
The fix was a distinction we'd missed: length caps are layout constraints, not safety rules. They should be applied, not used as grounds for refusal. Now long labels get trimmed, extra branches dropped, missing descriptions synthesised — while a card with no drawable content is still refused, with a reason a student can act on.
It says when it doesn't know
If the AI can't ground a diagram in something real, it says so instead of inventing one. A confident picture of something untrue is worse than no picture, because a student has no way to tell them apart. Everything AI-written carries a "Written by AI — check it" label.
It never asks what's wrong with you
The Tune screen changes type size, spacing and wording density live. It contains no question about diagnosis, reading level, or difficulty. It asks whether this looks better than that, with a live preview.
That's also architectural, not just polite: the reading profile is stored on-device and is never sent to the model. The model is asked where to cut text — never who is reading it. Research on disability-framed prompts shows measurably more stereotyping and more factual errors, so the cleanest mitigation is to never let the model form a view of the person at all.
It costs a student nothing
Runs on free OpenRouter models. The whole AI setup is a key pasted into the app — no terminal, no config file.
How we built it
React + TypeScript + Vite, with tldraw for the canvas. Diagrams are described in a small declarative shape dialect and converted into real tldraw shapes, so the arrows are properly bound to nodes and tldraw routes them itself.
The board layout is a pure function that places each new diagram in whichever direction is free — right, below, above, left, then diagonals — scoring candidates for compactness. Being pure means the "no two diagrams ever overlap" property is asserted directly in tests rather than hoped for.
152 unit tests and 23 integration tests. The integration suite runs the real server against a mock model provider and asserts behaviour under failure: a model that deletes content is rejected, truncated JSON returns an error instead of crashing, an error hidden inside a 200 response is surfaced, and a model with no tool-calling endpoint still produces a usable answer.
Challenges
Our own safety rules were breaking the product. Described above — the validator was correct about danger and wrong about untidiness, and it took reading real failures to see the difference.
Free models are genuinely awkward. Several have no tool-calling endpoint and answer 422 "no endpoints found that support tool use". Telling a student to go pick a different model is a bad answer, so the client now retries with the schema written into the prompt instead — and both paths land in the same validators, so the safety properties don't change.
Rate limits produced a silent wrong behaviour. We fired the explanation and diagram requests in parallel; free tiers allow roughly one at a time, so the diagram 429'd and fell back to a stale message while the text survived. The result was a page of text — the exact opposite of the product. Now sequential, diagram first, with a retry on 429.
Layout that looked fine and wasn't. Diagrams were placed strictly left-to-right, which after four questions was a 19:1 ribbon zoomed to nothing. The fix scores every free position for compactness instead of taking the first that fits.
What we learned
The most useful thing was that an accessibility rule and a product rule can be the same rule. "The diagram must carry a written description" started as a screen-reader requirement and turned out to be the thing that lets the app degrade gracefully when a drawing fails. "Never send the profile to the model" started as a privacy instinct and turned out to be the strongest defence against disability bias in model output.
The second thing: failing safe isn't enough if failing safe is invisible. A fallback that silently produces a worse experience reads to the user as a broken product. Every failure path in this app now says what happened and what to try.
What's next
- Testing with more neurodivergent students, across more subjects than science
- Saving and reopening a board, so a session survives closing the tab
- Voice input, for students who find typing the barrier
Built With
- css
- node.js
- openrouter
- playwright
- react
- tldraw
- typescript
- vite
- vitest
- web-speech-api
- zod