Teachers use a surprising number of small web tools in a single lesson: a wheel to pick a student, a group maker, a seating chart, a timer, a noise meter. The frustrating part is that most of them make you retype your class list every time, and many show ads right on the projector in front of thirty students.
So I built Classroom Wheels: a free set of classroom tools that all share one class list. You save your class once, and the name wheel, random name picker, group and team generators, seating chart, timers, noise meter, bingo cards and scoreboard all use it. Here's how it works under the hood, and a few decisions that turned out to matter.
1. No backend at all
The whole site is a static export of Next.js (output: "export"), served as static assets from Cloudflare. There are no API routes, no database and no accounts. Every page is pre-rendered HTML, and the interactive parts are small client components.
That decision did a lot of work:
- Hosting is free and fast everywhere, because it's just files on a CDN.
- Privacy is simple to explain. There's no server that could store student names, so schools don't need a long data-protection review.
- Search engines and AI crawlers get full HTML, because nothing important depends on client-side rendering.
2. Class lists live in the browser
Class lists are stored in localStorage through a small Zustand store with persist. Two lessons learned:
- Shared school Chromebooks often wipe storage, so every class can be exported to a backup file and imported again (JSON for everything, CSV for a single class).
- Storage can be unavailable (private windows, locked-down devices). Zustand quietly disables persistence if
localStoragethrows, which broke hydration for me, so the store uses a storage wrapper that never throws and the app keeps working in memory.
Absent students are marked once and every tool leaves them out.
3. Fair randomness
When a wheel picks a student, students notice if it feels rigged. Every pick, shuffle and spin uses crypto.getRandomValues (Web Crypto) rather than Math.random(). The wheel chooses the winner before it starts spinning and the animation just lands on that slice, so every name has exactly the same chance. The name picker also has a "no repeats" mode: nobody is picked twice until everyone has had a turn.
4. A noise meter that never records
The noise meter uses the Web Audio API to turn the microphone signal into a single loudness number about 20 times a second, on the device, and then throws the audio away. Nothing is recorded or uploaded. It shows a traffic light or "voice levels" (0 silent to 4 presenting), and it can calibrate to the room's normal hum so it doesn't turn red when the class is quiet.
5. Present mode
Every tool has a Present button (or the F key) that shows only the tool, full screen, in large type, with no menus or ads. Inside the Fullscreen API that meant a few edge cases: Esc exits fullscreen in most browsers, so tools that use Esc for their own pop-ups listen in the capture phase and call preventDefault() first.
6. Small, fast pages
I set a budget of 180 KB of gzipped JavaScript per page and a script checks it on every build. The framework takes most of that, so each tool's own code has to stay small: no heavy UI or animation libraries. Lighthouse on mobile scores 86–98 for performance and 100 for accessibility.
7. Tests that match how teachers use it
Playwright tests every tool at a Chromebook size (1366×768) and a phone size, including a check that the header and menus fit on a 320 px screen. A build-time SEO check fails if any page has a duplicate or over-long title, a wrong canonical URL, or FAQ markup that doesn't match the visible FAQ.
Try it
It's free and needs no signup: classroomwheels.com. A few pages teachers seem to like:
- Name wheel: spin to pick a student, winners come off so everyone gets a turn
- Random group generator: keep chosen students apart and mix ability levels
- 20 ready-made wheels: brain breaks, sight words, times tables, would you rather and more
If you teach, or build tools for teachers, I'd love to hear what's missing.
Top comments (0)