NextSpark
Printable hand-crank generator kits that teach students how electricity is made.
SolidWorksBambu StudioBambu Lab A1 (FDM)React 18TypeScriptViteTailwind CSSshadcn/uiSupabase (Auth, Postgres, RLS, Edge Functions)DenoStripe CheckoutResendVercel
Problem
High-school STEM mostly lands as theory and worksheets. A student who wants to build what they learned in class finds sourcing parts confusing and online projects too complex, and gives up before starting. NextSpark, an Enactus SFU project, set out to fix that with low-cost, 3D-printable kits students assemble themselves, starting with a generator that turns electromagnetic induction into something you can crank.
What I built
The first kit is a hand-crank generator: rotor, stator ring, coil holders, casing halves, gears and crank were modelled in SolidWorks, sliced in Bambu Studio and printed on a Bambu Lab A1, then assembled with bar magnets, hand-wound copper coils and bearings. The pilot workshop is scripted end to end: in groups of two or three, students wind 200 turns onto each of six coil holders, load the rotor with alternating-polarity magnets, fit the 3:1 gear pair, light an LED and read the output on a multimeter, then rerun with 12, 8 and 6 magnets and at different crank speeds to see Faraday's law move the needle. The session runs from a slide deck with checkpoint quizzes, an assembly guide and a troubleshooting sheet, with a breadboard mini-kit series (button, photoresistor, RGB colour mixer) for younger groups. Alongside the hardware sits a kit-store and learning platform: Supabase auth and Postgres with row-level security, Stripe Checkout through Deno edge functions, single-use access codes that unlock step-by-step lesson videos, per-lesson progress, and an admin dashboard for kits, lessons, orders and codes.
Highlights
- The experiment is designed into the parts: the rotor was modelled with 6-, 8-, 10- and 12-magnet variants and the crank gear as a separate print, so a class can change magnet count and gearing and measure the difference instead of reading about it.
- Treated the kit like a product, not a prototype: parts iterated from v2.0 to v3.1 (casing, stator ring, extended coil holders, gear handle), STLs shipped as ready-to-print Bambu Studio plates, and a README-mandated "Best Version" folder in the parts repo so any teammate can print a full kit without guessing which file is current.
- Access control lives in the database, not the client: every table is under RLS, lesson rows and their video links are readable only by enrolled users or admins, and codes are redeemed only through a SECURITY DEFINER RPC that row-locks the code and is idempotent for the same user, so students can never read the codes table.
- Payments hardened against the usual footguns: prices come from the DB rather than the cart, the webhook verifies Stripe's signature and is idempotent on the session id, one code is minted per unit with a retry on unique-key collision, and order status requires the owner's JWT because a session id in a URL is not proof of ownership.
- Access codes use a 31-character alphabet with I, L, O, 0 and 1 removed, so a code read aloud in a classroom or typed from a printed card cannot be mis-keyed.
Numbers
- Rotor modelled in 6-, 8-, 10- and 12-magnet variants; kit parts iterated from v2.0 to v3.1
- Pilot workshop: groups of 2-3, 200 turns per coil holder across 6 holders, 3:1 gear ratio, test at 12, 8 and 6 magnets with LED and multimeter, three checkpoint quizzes
- Sliced for a Bambu Lab A1 at 0.2 mm layers, 0.4 mm nozzle, honeycomb infill (per-object override, 60%), five gears per plate
- 12-slot rotor mesh is 54.4 x 54.4 x 20 mm; casing is 110 x 133 x 55 mm; stator ring 90 x 90 x 25 mm
- 8 tables, all with row-level security, plus one SECURITY DEFINER redeem RPC that row-locks the code; follow-up migration pins role/email against self-escalation
- Checkout ships to CA and US; quantity clamped to 1-20 per line; up to 5 retries on access-code collision; order-status requires the caller's JWT
- Access codes drawn from a 31-character unambiguous alphabet in NS-XXXX-XXXX form
Screenshots



