I'm a self-taught frontend developer from Morocco. Over the past months I built SkillBloom, a learning platform for kids in grades 1–6, aimed at homeschool families in the US.
It has 300 courses across Math, English, Science, Social Studies and Art, around 4,400 quiz questions, a parent dashboard, a weekly planner and PDF progress reports. I built it alone, with a lot of help from AI coding tools.
This post is about the technical side: the stack, a few problems I didn't expect, and some honest numbers from the first week after launching the domain.
The stack
Next.js 16 + React 19 + Tailwind CSS for the app
Supabase for auth and the database, with row-level security (RLS) everywhere
Paddle for subscriptions (it acts as the merchant of record, which matters a lot when you're selling to the US from Morocco)
Vercel for hosting
Courses as data files, validated at build time, so a broken lesson fails the build instead of breaking for a child
Storing courses as data instead of in the database was one of the best early decisions. Every course is a typed file. The game screens, the public SEO pages, the narration script and even my video tool all read from the same source.
Problem 1: the voice sounded robotic
Every lesson and question is read aloud, because 6-year-olds can't read long instructions. I started with the browser's built-in speech synthesis.
The first detailed feedback I got from a homeschool mom was: "The narration sounds distinctly robotic, and for young learners that can be make-or-break." She was right.
I had no budget for voice actors, so I used Kokoro, an open-source text-to-speech model (Apache 2.0 license) that runs locally through kokoro-js:
js
import { KokoroTTS } from "kokoro-js";
const tts = await KokoroTTS.from_pretrained(
"onnx-community/Kokoro-82M-v1.0-ONNX",
{ dtype: "fp32", device: "cpu" }
);
const audio = await tts.generate(text, { voice: "af_heart", speed: 0.9 });
audio.save(audio/${hash}.wav);
A few things that made this work at scale:
Hash-based file names. Each clip is named by a hash of text + voice + speed. Repeated lines like "Great job!" are generated once, and changing one sentence only regenerates that sentence.
Resumable generation. The script skips clips that already exist, so I could run it overnight and stop it any time.
A manifest + fallback. The player looks up the clip in a manifest and falls back to the browser voice if it's missing, so nothing breaks while generation is in progress.
fp32 was about 3× faster than q8 on my laptop, which surprised me.
The full set is around 19,000 clips (roughly 300 MB). The free courses alone took about 3.5 hours on a laptop CPU.
One unexpected lesson: my project lived inside OneDrive, and syncing thousands of audio files pushed OneDrive to about 16 GB of memory, which crashed builds. Keep big generated assets out of synced folders.
Problem 2: Google can't see content behind a login
All the games are behind sign-in, so Google could only see a handful of pages. The fix was a public page for every course, statically generated from the same course data:
What the child will learn (from the skill tags)
A real preview of the lesson text and key vocabulary
At-home activities parents can actually do
Three practice questions with "Show answer"
An FAQ with FAQPage structured data, plus Course and breadcrumb JSON-LD
That gave me 325 indexable pages, each with content that only that course has. The important rule I followed: no thin template text repeated 300 times. Every page pulls real, specific content from its course.
Problem 3: marketing content takes forever
Recording short videos of the games by hand was slow, so I built a small local tool with Playwright + ffmpeg:
Playwright opens the app in a 1080×1920 viewport and plays a scene automatically (answering questions correctly from the course data).
It records the screen.
ffmpeg adds the matching narration MP3s, a hook text for the first two seconds and an end card.
Now I describe scenes in a JSON file and get a batch of vertical videos in one run.
Honest numbers from week one
One Facebook post about a single free course: 40 parents asked for the link
Domain registered 4 days ago: Google already shows 38 different pages, with impressions growing each day (17 → 48 → 69 per day)
Most impressions come from searches like "imports and exports for kids", "what is a hemisphere for kids" and "7 8 9 times tables", which confirms the long-tail approach
Clicks from Google: almost zero so far, since most pages are still on page 2–3
Revenue: $0. My first founding-family offer goes out this week
What I'd do differently
Talk to parents before building the 300th course. Real feedback (like the voice) mattered more than more content.
Set up the domain and SEO pages earlier. Google takes time, and every week counts.
Keep generated files out of the repo and synced folders from day one.
If you're building something for parents or teachers, I'd love to compare notes. And if you have a kid in grades 1–6, you can try the free courses at skillbloomkids.com. Honest feedback is very welcome.