# Quiz Maker — turn any text into a quiz you can share (★★★)

Build a quiz maker. Paste a page of notes, or type a topic like "the water cycle", and a minute later there is a ten-question multiple-choice quiz. A Cloudflare Worker sends the text to a language model on Workers AI, checks that the answer is valid quiz JSON, and saves the quiz in KV under a short code. Send the link to friends: they play it, see their score and an explanation for each answer, and get on that quiz's top-10 leaderboard. The real lesson is where the model call lives: in server code, behind a binding or a secret, and never in the browser with a key anyone can copy.

## Done looks like

- The maker screen: a big text box (up to 8,000 characters) or a topic box (up to 100), a difficulty choice (easy, medium, hard), a maker password box and "Make quiz".
- While it works, the page says what is happening. Within about a minute it shows the new quiz's link, like `/q/H7KM2P`.
- Every quiz has exactly 10 questions, each with 4 choices, one right answer and a one-sentence explanation.
- The play screen shows one question at a time. After each answer it shows right or wrong and the explanation, then "Next". The end screen shows the score out of 10.
- Answers are checked by the Worker. The browser never receives the answer key before the player answers, so reading the page source does not help.
- After the last question, a player can enter a name (up to 12 characters) and, if the score makes the top 10, it appears on the quiz's leaderboard.
- Every quiz page says "Written by a model. It can be wrong."
- A wrong maker password gets "Wrong password" and no model call. Playing needs only the link.
- Live at the address `npx wrangler deploy` prints. No console errors.

## Constraints

- One Cloudflare Worker with static assets, a Workers AI binding (`AI`) and a KV binding (`QUIZZES`). Use wrangler. Free plan only.
- The model is called only from the Worker, through the `AI` binding. No API key appears in the front end or in the repo. If you later switch to an outside model API, its key goes in with `npx wrangler secret put` and stays on the server.
- Pick a text model from the Workers AI model list (developers.cloudflare.com/workers-ai/models). Prefer one that supports JSON output. Ask for JSON only, then parse it defensively.
- Validate on the server: exactly 10 questions; each has `q` (up to 200 characters), `choices` (exactly 4 different strings, each up to 80), `answer` (0 to 3) and `explain` (up to 200). Trim and clean the text. If it fails, retry the model call once, then give a clear error.
- Workers AI has a free daily allowance. Protect it: making a quiz needs `MAKER_PASSWORD`, a Worker secret set with `npx wrangler secret put MAKER_PASSWORD`. Cap the input length before calling the model.
- KV keys: `quiz:<code>` holds `{title, difficulty, created, questions}`, written once. Do not store the pasted text, only the questions. `board:<code>` holds the top 10 as `[{name, score, ms}]`.
- KV allows 1,000 writes a day on the free plan and is eventually consistent, so write the board only when a score makes the top 10. Note in a comment that two scores at the same moment can overwrite each other, and that a Durable Object or D1 would fix it.
- Codes: 6 characters from an alphabet with no lookalikes.
- Routes: `POST /api/quiz` (make), `GET /api/quiz/<code>` (questions without answers), `POST /api/quiz/<code>/check` (`{i, choice}` returns right or wrong, the right answer and the explanation), `POST /api/quiz/<code>/finish` (all choices; the Worker counts the score), `GET /api/quiz/<code>/board`.
- Tell the model in the prompt: questions only about the given text or topic, no personal details about real private people.
- Front end: plain HTML, CSS and JavaScript, no frameworks, no build step. No accounts, no tracking. Phone works.

## Milestones

1. **A player with a fixed quiz.** The play screen working from a hard-coded quiz object in the front end. No server yet.
   Check: you can play all 10 questions, see an explanation after each, and get a score at the end.
2. **A Worker.** Set up the Worker with static assets serving the player, and `GET /api/health` returning `ok`. Run it locally.
   Check: the quiz plays from the local wrangler address and `/api/health` shows `ok`.
3. **First model call.** Add the `AI` binding. `POST /api/quiz` sends a topic to the model and returns the raw text. Show it on a test page.
   Check: the topic "volcanoes" returns text that looks like quiz questions about volcanoes.
4. **Valid JSON or a clear error.** Ask for JSON only, parse it, validate every field, retry once, and turn failures into a short message.
   Check: ten topics in a row each give exactly 10 valid questions or a clear error, and the page never shows a broken quiz.
5. **Password and go live.** Add `MAKER_PASSWORD`. Deploy. Make a quiz from your phone by pasting class notes.
   Check: a wrong password is refused without a model call (nothing new in the Workers AI usage), and the right one makes a quiz from your phone that you can play there and then.
> A quiz now lives only in the tab that made it. The next two keep it under a short code so anyone with the link can play, and keep the answers on the server.
6. **Save and share.** Store the quiz in KV under a code. Serve `/q/<code>`. Move answer checking to the server with the `check` and `finish` routes, and remove answers from what the browser gets. Deploy again.
   Check: open the link in another browser and play it; search the page source and network responses for the answers before answering: they are not there.
7. **Leaderboard.** Name entry at the end, top 10 per quiz in KV, shown on the end screen.
   Check: three players with different scores appear in the right order, and an 11th lower score does not write to KV.

## Stretch

- "Report a wrong answer" on each question, stored as a count in KV, shown to the quiz maker.
- A timer mode where faster right answers score more, scored on the server.
- Quiz from a web page address: the Worker fetches the page, strips it to text and caps the length.
- Picture questions using the photo-wall approach (R2 plus signed addresses).
- Move leaderboards to a Durable Object so simultaneous scores never collide.

## Leave out

- Accounts, classes, teacher dashboards.
- Any model call or API key in the browser.
- Storing the text people paste.
- Tracking, ads.

Start by reading WRITING.md and the guide chapter on data if you need storage.
