ACT trainer

A math practice app I built for my son's ACT prep, continuously improved as we learned more from his usage.

Shipped React TypeScript Tailwind Supabase Netlify Functions Claude Haiku 4.5 PWA
Table of Contents

What it is

An ACT math practice app built for one student, my son. It started as a way for him to practice math word problems, the questions his ACT test reports showed were giving him the hardest time. It grew to cover more topics, eventually spanning the entire math section of the test. It runs on his phone, his iPad and his laptop: short drills with a clock on every question, an explanation the moment an answer is wrong, a full 45-question practice session, and a dashboard that we both read.

Phone screen of a geometry drill question at 46 seconds, with the timer ring turned amber.

A drill question at 46 seconds: the ring has turned amber.

Phone screen after a wrong answer: the correct choice in green, the picked choice in red, the stock explanation, a second explanation built around the wrong choice, and four buttons asking what went wrong: content gap, misread, careless, timing.

A miss: the right answer, a second explanation built around the wrong choice, and the reason picker.

Screens on this page are staged with made-up questions, a made-up student and a faked backend: no real data.

Credit where credit is due

I take no credit for my son’s improved scores. He did the work and put in the study hours. He sat through the drills with the app and let me know when something was wrong or could be improved. He also worked with a tutor, remotely, throughout the whole prep, on both his math and his test-taking skills.

The app was one tool next to those two. This page is about the tool: what I saw, what I changed, and what happened next. I hope it gives you inspiration to build something.

How I built it

How every change on this page happened
  1. 1 See it

    A pattern in the data, a line in a test report, or something he said after a session.

  2. 2 Build

    A change to the app, usually the same day, tested before it reached his phone.

  3. 3 Watch

    His next sessions, read for whether what he did changed.

  4. 4 Adapt

    Keep it, retune it, or take it out. Then look again.

The first version came out of a written spec in one weekend. Everything after it came from watching him use it.

What changed, and when (2026)
  1. Jul 12 Build

    Version one, in one unattended agent run: drills with the pace clock, a review schedule for misses, a reason on every miss, a full practice session, and a photo-to-question importer.

  2. Jul 17 Rewards Questions Reports

    Credits and badges. New questions drafted from the ones he missed: same idea, different numbers. Session summaries list every miss; the practice session gets an answer eliminator, a review screen and a time breakdown.

  3. Jul 29 Questions

    Diagrams and whole practice tests: figures are stored and shown with the question, and a full test loads in one sitting.

  4. Aug 14 Questions

    Missed questions due for review come back inside the regular drills.

  5. Aug 29 Reports Rewards Time

    Topics follow the ACT's own score-report categories. Credits can buy things, the multiplier follows how many of the last seven days he practiced, the speed bonus pays for 30 to 67 seconds, and a bail prompt appears at 90.

  6. Sep 6 Order

    Practice sessions ramp from easy to hard, like the real test, and prefer questions he hasn't seen.

  7. Sep 9 Reports

    Every answer in a practice session records its position, so we can see where in the session the time went.

  8. Sep 17–21 Time

    No code: fresh calculator batteries, spares in the bag, and one rule for the last week. When the bail prompt fires a second time, move on.

  9. Sep 26 The test

    Test day.

The first version

I wrote the spec in July. I wanted my AI coding agent to build it in one unattended run, so a lot of work went into the planning phase: which platforms we would use, what permissions were needed, which accounts needed creation, which API keys needed to be set up. I wanted the AI to work through completion, without having to stop for me to set something up.

With the plan in place, the initial run began, going through six phases, each one gated by automated tests: 35 of them by the end of the run. Each phase had instructions on what to do if it encountered a road block, how to steer around it, and how to document any blockers. No real blocks were hit and version 1.0 was ready in the morning. Never underestimate the value of planning ahead.

The first version offered five ways to practice: drills weighted toward his weakest topics, drills by topic, a ten-question sprint, a review queue that brings misses back on a schedule, and a 45-question practice session on one 50-minute clock. Every miss asks for a reason in one tap (didn’t know it, misread it, careless, ran out of time), because each of those calls for a different fix.

The bank started at 30 questions. An AI importer turned a photo or the text of a single missed problem into a draft question, which I checked before it went in. An “explain it differently” button rewrote the explanation of a wrong answer around the choice he had picked.

Feeding it questions

Adding one problem at a time was too slow. A practice test has 45 questions, and about one in five comes with a figure. So the import moved from single problems to whole tests: each practice test went in as a PDF and came out as 45 questions with their diagrams, shown on the question the way they appear on paper. This allowed me to photograph the practice tests in the book (or download practice PDFs) and import them at once. By late August the bank had 260 active questions, most of them from five full practice tests.

Phone screen of a trig question at 24 seconds: right triangle PQR with the right angle at Q, legs labeled 7 and 24, a note that the figure is not drawn to scale, and four fraction choices.

A question with its figure, shown on the question the way it appears on paper.

But a fixed bank has a second problem. Answer the same question twice and you start remembering the answer instead of solving it. So I had the app draft new questions from existing ones, the same idea with different numbers, and a question he missed could come back as a new one. Each draft waited for my review before it went into the bank, the same as every other import.

Admin screen of the AI importer: a draft question about a rectangular garden with its four choices, topic algebra, difficulty medium, and a live preview of the question as the student sees it, with the explanation naming the trap.

The importer’s draft: every field editable, a live preview, and nothing saved until I save it.

Matching the actual test reports

What started as a tool to practice math word problems evolved into a full ACT math test simulator.

The ACT reports math by category, and most of the word problems he found hardest fall under one called Integrating Essential Skills: rates, ratios, percentages and unit conversions. The app had filed them all as “word problems,” so its dashboard and the official reports didn’t line up. In August I added the ACT’s category and re-categorized 31 questions. From then on the app’s topic table and the official reports used the same terms, and the drills could aim at what the reports said.

The reports inside the app grew the same way. The session summary lists every miss with its explanation and calls out the time sinks, questions that were both wrong and over pace. The dashboard has a weekly fix card built from his most common reason for missing, and a list of bail candidates. After a practice session, a “where the clock went” table sorts the questions from slowest first.

Two dashboard cards. This week's fix: timing pressure is this week's pattern, run a sprint. Bail candidates: three questions that were over the 67-second pace and wrong, at 214, 168 and 131 seconds.

Two dashboard cards: the week’s fix, from his most common reason for missing, and the bail candidates.

Gamification

I decided to add rewards to incentivize him to use the app every day. Credits come from his answers: two for a right answer, a bonus for a quick one, and one for a wrong answer he tagged with a reason, because tagging every miss is the study habit. A multiplier rewarded streaks of consecutive days, and six badges marked milestones such as a first full practice session.

By late August the data showed two problems. Practice came in bursts with long gaps between them, and both long gaps began the day after a streak broke: the multiplier was a cliff, and one missed night wiped out a week. He would practice a few days in a row to chase the streak, then miss a day and stay away from the app for a week or more. And the credits bought nothing, because there was nothing to spend them on yet.

So I adapted the multiplier to follow density, the number of days he practiced in the last seven. Three days earn 1.5 times, five earn double, and one skipped night costs nothing. A catalog of real rewards went in; he asks to redeem, and I approve. The speed bonus changed too, for reasons explained in the next section. That change recalculated his history and would have taken back credits he had earned under the old rule, so we granted them back. That was a parenting call, not an engineering one.

Rewards bring someone back to an app. Ten-question evening drills are great, but the real findings and improvements came from the full-length, timed practice sessions.

Rewards page: a credit balance with the note practiced 7 of the last 7 days, earnings times 2; a catalog of five rewards, three redeemable; and six badges, four earned, including In the Pocket for answers at 30 to 67 seconds.

The rewards page: the balance and its multiplier, the catalog, and the badges.

Time: the problem we didn’t see at first

His tutor said he rushed. The app said the opposite: in drills he averaged well under the 67 seconds a question that ACT pace allows, and rarely went over.

His ACT test reports settled it, because they timed every question. On the math section of his April test he used a minute more than the 50 allowed. One question took almost four minutes, and he missed it anyway. Four missed questions took over 100 seconds each, about ten minutes together, which left too little time for the last stretch: he had to rush those questions, close to answering at random, and the final one took seconds. His quickest answers and his slowest were both his least accurate, and the ones in between were his best.

So the tutor was right that he rushed, and the report showed when: at the end, after spending too long on a few questions earlier. A ten-question drill on a phone can’t show that. His drill pace was about half his pace on a real section, because the drill was measuring a different activity.

That changed what the clock is for.

One question on the app's clock, at ten times speed
What the clock shows
calm
amber
red
bail
What earns the speed bonus
July: 45 s or less
August on: 30 to 67 s
0 30 45 67 90

Thresholds from the app's pacing file. 67 seconds is the ACT's own pace: 45 math questions in 50 minutes. The clock never submits an answer for him; every state is a prompt to decide.

  • Amber at 45 seconds, red at 67. These were there from the start. The clock never submits an answer on its own: deciding to move on is the skill being practiced.
  • A bail prompt at 90 seconds. It says out loud what the color only hinted at, that this question is costing him the end of the section, and skipping is one tap. In the April section, every question that went past 100 seconds was one he missed.
  • A different speed bonus. In July it paid for answering within 45 seconds, which rewarded speed. His answers under 30 seconds were his least accurate, so from August the bonus pays for 30 to 67 seconds: deliberate, and inside the budget. The badge called Speed Demon became In the Pocket.
Phone screen at 1 minute 31 seconds on a question, red ring, with a banner: This one is costing you the end of the section. Best guess and move. A button: Skip it and move on.

The bail prompt at 91 seconds. Skipping is one tap.

Making practice look like the test

The first full practice session, in early September, looked great: he finished with nine minutes to spare. The draw explained it. The app picked 45 questions at random from the whole bank, so the hard ones were scattered evenly, and 19 of the 45 were questions he had already seen. A real ACT math section runs from easy to hard, and the closing stretch was exactly where previous tests had gone wrong.

Where the hard questions sit in a practice session
Easy Medium Hard
Before: 45 questions at random from the whole bank
Opening third
4.3
6.2
4.4
Middle third
4.3
6.2
4.4
Closing third
4.3
6.2
4.4
After: drawn by thirds, easy to hard, unseen questions first
Opening third
8
6
1
Middle third
4
8
3
Closing third
1
5
9
Questions per 15-question third. Before: the average a random draw gives, from the bank's mix of 75 easy, 108 medium and 77 hard questions. After: the app's ramp table, 13 easy, 19 medium and 13 hard in total, the same mix as before. Only the order changed.

So now a practice session is drawn by thirds, easy to hard, with unseen questions first. The mix of easy, medium and hard stays the same; only the order changed. The next practice session took 50.7 minutes, within 20 seconds of the April test. For the first time, practice reproduced the real thing.

Minutes used on 45 math questions: his April ACT section, then three practice sessions
His April ACT math section 51.0 min
the real-test baseline
First practice session, random draw, Sept 6 41.2 min
hard questions scattered; nine minutes of false slack
Second practice session, ramped, Sept 9 50.7 min
within 20 seconds of the April test
Third practice session, ramped, Sept 15 51.3 min
49.2 had he bailed its one 3½-minute question at the 90-second prompt
Sources: the app's attempt log for the three practice sessions, his April ACT test report for the first row. Amber bars ran past the limit.

But I still couldn’t see where in a session the time went. The app saved all 45 answers at once at the end, and they reached the database in random order. Now every answer records its position. Anything worth analyzing later has to be saved when it happens.

The third practice session was the first with position data. The rush at the end was gone: the closing stretch was steady, and he worked the last question through instead of guessing it. What remained was one question in the middle that ran three and a half minutes. Bailing it at the 90-second prompt would have brought the session in under 50 minutes. So the rule for the last week was one line: when the bail prompt fires a second time, move on.

Tablet screen of a 45-question practice session at question 31 with 9 minutes 28 seconds left in amber; two choices struck out with the eliminator, flags on several questions in the palette.

A practice session: one 50-minute clock, flags, and an eliminator to strike out choices.

Table titled Where the clock went, listing question number, topic and time, slowest first, from 214 seconds down to 83.

After the session: where the clock went, slowest first.

Test day

He took the test in late September. By his account the math section was harder than he expected, but he managed his time and finished with a few minutes to spare. He said it was the first time he could answer every question without rushing at the end.

At the start of the prep we set a target score, and it sat on his dashboard as a goal line the whole time. He hit it. It was his best test overall, across every subject, gave him his best superscore, and moved him into the tier he was aiming for.

That result is his. He did the work. His tutor built up his math and his test-taking skills, and those make him faster too, because a question you know how to solve takes less time. The app covered only math; English and Reading were his and the tutor’s.

Afterward I asked him how he thinks the app helped, if it did. He named two things. It made him more aware of time management. And because he had seen so many different problems, many of them several times, he had a better feel for which ones were difficult and how to tackle them efficiently.

That matches what I believe. Time was the problem we set out to fix, and on test day time wasn’t an issue. I think the time awareness helped throughout the test: moving on had become a habit he practiced on every question, so on the day it wasn’t a decision he had to make up under pressure. I can’t separate that from his own work or the tutor’s.

What I’d keep

  • Measure the real activity. Drill pace and section pace turned out to be different things.
  • Pay for the behavior you want. Speed was easy to count and the wrong thing to reward.
  • Leave cliffs out of incentives. One missed night shouldn’t erase a week.
  • Make practice look like the test, down to the order of the questions.
  • Save what you will want to analyze at the moment it happens.
  • When a rule changes, keep what was earned under the old one.

The tech stack

An installable web app in React, TypeScript and Tailwind that keeps working offline and syncs when it reconnects. Supabase holds the data, and its row-level security is the boundary: he can read only active questions and his own answers, and an answer can be added but never edited or deleted. The AI features run on Claude Haiku 4.5 through Netlify functions, so the API key never reaches the browser, and each user has a daily cap.

An AI coding agent wrote the code from the spec and from each change after it. I set the direction, reviewed every change before it reached his phone, and decided what shipped.

What’s next

The app has been resting since the test. I may open it to other students; if I do, this page will say so.

Updates

  • November 4, 2026: page published.

Let's Connect