Skip to content

Repository files navigation

Joju — a procedural Montessori math and life-skills PWA

A procedural, Montessori-inspired K–4 math game with a social-emotional learning (SEL) companion, built solo for one kid. Offline-first PWA, runs on a phone, reads itself aloud, and never serves the same problem twice.

Joju demo


The story

It started on a weekend morning. Joju was three taps into a math app I had just paid for when a crown icon appeared on the button they wanted to press next, and a fifteen-second ad for another app started playing while they waited. I watched my kid's face go blank.

I counted the apps on the tablet that month. Four of them charged nine to fifteen dollars each. None of them taught anything my kid hadn't seen the week before; the same two hundred questions rotated in a new coat of paint. The "premium" content was mostly the same content, unlocked.

That night I opened a blank Vite project and started writing a generator that would never run out of problems. Joju picked the mascot. This repo is the result.

The problem with most kid learning apps

  • Subscriptions and ads. The best features sit behind a crown icon, and the wait between levels is a slot for another app's ad. That model optimizes for parent retention, not for the child.
  • Finite content. Even the big-name apps ship a fixed bank of a few hundred questions per topic. Kids memorize answers, not skills, and the "progression" is mostly cosmetic (new backgrounds, new avatars, same math).
  • Noise. Slot-machine rewards, streaks designed to create guilt if you miss a day, and constant coin-drop sound effects make the screen feel like a casino, not a classroom.

How it helps Joju

  • It does not run out. The engine in src/engine/proceduralGenerator.ts generates infinite unique activities, seeded by the child's profile and the date. The same kid sees fresh content every day; a sibling on another device sees the same content. No server required.
  • It reads itself aloud. A pre-reader can play independently. A local voice proxy (server/voice-proxy.mjs) narrates every instruction through free Edge TTS by default, with Azure or OpenAI as opt-in upgrades.
  • It moves at Montessori pace. Three-period lessons from src/engine/montessoriCurriculum.ts shift the instruction style — "Look!", "Show me", "What is this?" — based on how well the child already knows the topic.
  • It names feelings, not only numbers. The SEL mini-games in src/engine/selEngine.ts teach emotions, kind choices, and step sequences alongside math.
  • Parents keep the dials. The Parent Zone mutes voice, turns off celebrations, switches to a gentler mascot, and respects accessibility needs (reduced motion, larger text, literal language, sensory-friendly mode) via src/engine/autismSupport.ts and src/engine/focusEngine.ts.
  • Nothing leaves the device unless the parent opts in. IndexedDB is the default store. Firebase sync is a toggle, not a default. No analytics SDKs, no trackers.

How it is different from other learning apps

  • vs Khan Academy Kids and ABC Mouse. Joju has no accounts, no ads, no tracking, and no paywall. You can clone the repo, run it locally, and your kid plays in five minutes.
  • vs Prodigy and similar "math RPGs". Here, math is the game, not the toll you pay for another combat turn. Celebrations are bounded, streaks are soft, and nothing buzzes at you for missing a day.
  • vs Duolingo ABC. Duolingo's model is spaced repetition over a fixed tree of lessons. Joju's model is a procedural generator that re-rolls from templates plus emoji pools, so a child with a hundred hours in the app has seen a hundred hours of different problems.
  • vs DragonBox, Numberblocks, Khan Kids math. Those are polished and beautiful, and they all ship with a finite content bundle. Joju's bet is that the engine is the product. Swap the curriculum JSON and you have a different age group or a different language.
  • vs every other kid app. Dual-licensed. Code MIT, content CC BY-NC 4.0. Teachers and homeschool parents can use, remix, and share. Companies cannot repackage the content and sell it.
flowchart LR
  otherApps["Most kid apps<br/>fixed content bank"] --> grind[Child memorizes answers]
  joju["Joju<br/>procedural engine"] --> fresh[Infinite unique activities]
  fresh --> skills[Child builds skills, not answers]
  grind --> churn[Parent cancels subscription]
Loading

Why this might be interesting to developers

  • Client-side procedural generator in src/engine/proceduralGenerator.ts (~2,600 lines): templates, emoji pools, distractor logic, quest wrappers, difficulty tiers, all seeded so offline works and two devices on the same profile agree on content.
  • Montessori three-period lesson flow in src/engine/montessoriCurriculum.ts wraps each instruction depending on the child's mastery level.
  • SEL mini-games in src/engine/selEngine.ts power emotion-labeling, scenario choice, and sequence puzzles.
  • Voice pipeline with an emoji-to-word sanitizer so "🍇 fly away" is never read as "purple-heart fly away", and a proxy that falls back Edge TTS → Azure → OpenAI.
  • Offline-first PWA with IndexedDB progress, optional Firebase sync, and a Parent Zone for per-topic stars, accessibility toggles, and gentle / celebration mode.

Full disclosure: this was shipped solo, is not actively maintained, and is being open-sourced as a portfolio-grade case study more than a product. See Status and limitations below.

Architecture

flowchart LR
  parent[Parent or child user] --> pwa[React 19 PWA<br/>Vite 8]
  pwa --> engine[Procedural engine<br/>src/engine]
  engine --> activities[Generated activities]
  activities --> games[Game components<br/>src/components/games]
  pwa --> idb[(IndexedDB progress)]
  pwa --> fb[(Firebase auth and sync)]
  pwa -->|TTS requests| voice[Voice proxy<br/>:8787]
  voice -->|edge or OpenAI or Azure| tts[External TTS]
  pwa -->|content fetch| content[Content server<br/>:8788]
Loading

Three processes run locally in dev (npm run dev starts all three via concurrently):

Port Process Source
5173 Vite dev server vite.config.ts
8787 Voice (TTS) proxy server/voice-proxy.mjs
8788 Content API server/content-server.ts

Tech stack

  • React 19 + TypeScript 5.9 + Vite 8
  • Firebase (auth + Firestore + optional Cloud Functions) — functions/
  • IndexedDB via idb for offline progress
  • Zustand for client state
  • node-edge-tts for free neural TTS; Azure Speech and Azure OpenAI as opt-in backends
  • Phosphor Icons + custom SVG + a Rive mascot
  • Vitest + Testing Library + happy-dom for unit tests

Run locally

git clone https://github.com/<your-user>/joju-learn.git
cd joju-learn
cp .env.example .env
cp functions/.env.example functions/.env.local      # only if you want Cloud Functions
npm install
npm run dev

Open http://localhost:5173. You can run without a Firebase project if you leave the VITE_FIREBASE_* values blank — the app falls back to local IndexedDB and the child-select screen.

Useful scripts:

npm run dev          # vite + voice proxy + content server
npm run dev:vite     # just the Vite app (no voice, no content server)
npm run build        # tsc -b && vite build
npm run preview      # preview production build
npm run lint         # ESLint

Repo tour

src/
  app/           app shell, router, providers
  components/    reusable UI (auth, brand, game, games, kid, layout, monetization, parent, story)
  content/       curriculum and topic definitions, strongly typed
  engine/        procedural generator, SEL engine, Montessori curriculum, mascot dialogue, rewards
  lib/           small helpers (Firebase client, IDB, etc.)
  pages/         top-level routes (GamePage, SelActivitiesPage, SandboxPage, Parent pages, …)
  services/      audio, TTS sanitization, analytics, settings, sync
  store/         Zustand stores
  styles/        global + per-feature CSS

server/          node processes for dev (voice proxy, content API)
functions/       Firebase Cloud Functions (LLM-backed activity generation)
public/          icons, mascot, PWA manifest

The most interesting file if you like procgen is src/engine/proceduralGenerator.ts (roughly 2,600 lines of templates, pools, distractor logic, quest wrappers, and difficulty tiers).

Status and limitations

This is a portfolio / learning release, not a product roadmap.

  • Not actively maintained. Issues are welcome but may not be addressed.
  • Pull requests that fix bugs or polish the existing surface are welcome; big new features are out of scope. See CONTRIBUTING.md.
  • Tested primarily on Chrome desktop and Safari iOS PWA.
  • TTS quality depends on your chosen backend. Edge TTS is free and surprisingly good; Azure / OpenAI cost money and are optional.
  • Firebase is optional — everything works against IndexedDB alone.

License

Dual-licensed, on purpose:

  • Code — MIT. See LICENSE.
  • Content and assets — Creative Commons Attribution-NonCommercial 4.0 International. See LICENSE-CONTENT.

See NOTICE.md for the short plain-English version.

Credits

Built solo by Binol George for his kid Joju. The goal was always: one parent, one child, one app that makes a difficult year a bit more playful. If any piece of this is useful to you — the procedural engine, the Montessori integration, the SEL scenarios, the voice pipeline — please take it, credit the project, and build something kind with it.

If you want to reach out, open an issue or find me on LinkedIn.

About

Procedural Montessori K-4 math and life-skills PWA. Built solo for one kid. Dual-licensed: MIT code + CC BY-NC 4.0 content.

Topics

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages