Documentation

How JANUS works

JANUS is a static web application: HTML, CSS and ES-module JavaScript, no build step, no server-side code, no third-party libraries. Everything described here runs in the browser.

Architecture

Three layers, each with one job:

  • App shell — the pages, stylesheet, code and lesson catalogue. Cached by a service worker so the application itself loads with no connection.
  • Content — four subject files of plain JSON (content/physics.json and so on). They are never read directly by the app; lessons reach the app only by being installed as a pack into IndexedDB. This is what makes "not downloaded" a real state rather than a label.
  • State — progress, quiz results, tutor history, the sync queue and the sync mirror in IndexedDB; presentation settings in localStorage under janus:v1. Every storage call is wrapped so a blocked or full store degrades to an in-memory session rather than an error.

The app page (app/) is a single-page router on the URL hash: #/library, #/lesson/<id>, #/quiz/<id>, #/problems/<id>, #/sims, #/packs, #/notebook, #/settings. Modules: settings, db, net, sync, content, retrieval, tutor, packs, progress, sims, illustrations, app.

Service worker

sw.js is registered from the app and the landing page with scope /hackathon/janus/. On install it precaches the shell file by file (a single missing file does not abort the install). On activate it deletes any janus-* cache whose name is not in the current set — caches are versioned as janus-shell-v1.0.0, janus-content-v1, janus-fonts-v1 — and claims open clients.

Fetch strategy: same-origin requests inside the scope are served cache-first with network fallback; a hit on a shell file also triggers a background refresh so updates are picked up on the next load. Google Fonts requests (the only external origin) are cache-first at runtime. Two query parameters are special: ?ping always goes to the network (reachability checks), and ?simoffline is answered from cache only or with a 503 (the "Simulate offline" switch). A navigation that fails while offline falls back to the cached app page.

The worker's state is shown in the strip under the app header: installing, ready, updated (with a "Reload to update" action that tells the waiting worker to take over, then reloads), unsupported or error. It also reports the version it is running.

Fonts: the stylesheet declares full system fallbacks (Fraunces → Georgia/serif, Nunito → Segoe UI/system-ui). If fonts never load, nothing in the layout depends on them. In low-bandwidth mode the font stylesheet is not requested at all.

Storage

IndexedDB database janus, version 1, with stores packs, lessons (indexed by subject), progress, results, tutor, syncQueue, synced, activity and meta. The wrapper in js/db.js is about 100 lines: one transaction per call, promise-based, with putMany/deleteMany for pack installs. The Settings page shows the measured size of every store and, where the browser supports it, the storage quota estimate.

Content packs

The pack builder takes subjects, difficulty levels, languages and a storage limit. It loads the subject files (from the network, or from the service-worker cache, or — if neither is reachable — from lessons already installed), removes Hindi fields when Hindi is not selected, and measures the byte length of each lesson's serialised JSON. Lessons are ranked Foundation → Standard → Advanced, then by subject and lesson order, and added greedily until the limit is reached. Simulation code is shared by all six simulations, so a lesson with a simulation is charged one sixth of the measured size of js/sims.js the first time that simulation appears in the pack. Everything that does not fit is listed as cut, with the reason.

Download to this device writes the lessons and the pack record to IndexedDB and messages the service worker to cache the subject files and simulation code so they are also available for future rebuilds. Export pack file produces a real .janus-pack.json download; Import pack file validates such a file and installs it — that is how a teacher moves a pack between devices with no internet. Installed packs are listed with size, version, date and origin, and can be removed; a lesson is deleted only when no remaining pack contains it.

Sync

Every progress write (section read, quiz result, problem solved) is also appended to syncQueue. The engine listens for connection changes and pushes the queue to a SyncTarget — an object with push(items) and status(). No server exists in this deployment, so the only target is LocalMirrorTarget, which copies each record into the separate synced store and records lastSyncedAt. The interface labels the target as a local mirror and says why. Queue length, last sync time and a manual "Sync now" are on the Notebook and Settings pages; syncing while offline (real or simulated) is refused with a message and the queue is kept.

Local tutor

The tutor is not an AI model. It is retrieval over the on-device knowledge base plus a small set of explicit intents:

  1. Text is tokenised (Unicode letters and digits, so Hindi works), stop-words removed, and lightly stemmed (plurals, -ing, -ed, -ly).
  2. Every installed lesson contributes passages: each section (both reading levels), each formula, glossary term, worked example, problem and constant. Headings and terms are weighted higher.
  3. Queries are scored with BM25 (k₁ = 1.4, b = 0.7). The best passage is quoted at the current reading level and language, with a link to its lesson.
  4. Confidence is the fraction of query terms found in the passage. Below one third, or when a lesson in the catalogue (which is always available) scores clearly higher than anything installed, the tutor says it could not find the topic in your downloaded packs and names the pack to download.

Intents, matched by pattern before search runs: "explain this in simpler language" (re-quotes the last passage at the simpler level), "explain in Hindi" (uses the Hindi text if the lesson has it, otherwise says which lessons do), "give me a harder / easier problem" (moves through the current lesson's problem set by difficulty), "I don't understand step N" (re-explains step N of the current worked solution at the simpler level and asks a check question that it then marks), "quiz me" (opens the current lesson's quiz), "what is X" (glossary lookup first, then search). History is kept in IndexedDB and can be cleared.

Content coverage

Generated from the live catalogue (content/manifest.json):

Lessons by subject with level, sections, quiz questions, problems and size
SubjectLessonLevelSectionsQuizProblemsSimulationSize
Loading the catalogue…

Every lesson contains: explanations at a standard and a simpler reading level, three to six key formulas, a worked example with standard and simpler step lists, a quiz mixing multiple choice and numeric questions (numeric answers are checked with a stated tolerance), three problems of rising difficulty with step-by-step solutions, glossary entries and, where relevant, physical constants. Content was written and checked by hand; the worked numbers are reproduced in the problem solutions so they can be verified.

Language coverage

Hindi coverage per lesson
LessonTitle & summary in HindiFull text in Hindi (both levels)Glossary in Hindi

English is complete for every lesson. Hindi coverage is partial and is shown as a count in the app rather than implied.

Simulations

  • Orbit — units with GM = 1 and planet radius 1; semi-implicit Euler with eight sub-steps per frame. Reports whether the orbit is circular, elliptical, escaping or crashing from the specific orbital energy ½v² − 1/r.
  • Projectile — closed-form ideal trajectory with g = 9.8 m/s², no drag; optional comparison with the complementary angle.
  • Reaction rate — particles in a box with speed ∝ √T; a collision "succeeds" with probability proportional to e−Eₐ/RT. The absolute probability is scaled up so successes are visible; the ratio between settings is the exact Arrhenius ratio and is displayed.
  • Lever — principle of moments, static.
  • Grapher — a hand-written recursive-descent parser (no eval) supporting + − × ÷ ^, brackets, implicit multiplication, sin cos tan sqrt abs exp ln log, pi and e; plots y = k·f(x − h) + a against the original.
  • Circuit — Ohm's law, series/parallel, power.

All simulations use native range/select inputs (keyboard-operable), announce results in a live region, start paused when reduced motion is on, and drop to 12 frames per second at reduced resolution in low-bandwidth mode.

Accessibility

  • Semantic landmarks, ordered headings, a skip link, visible focus rings, no keyboard traps; the tutor panel and every dialog-like region is reachable by Tab.
  • Connection state uses text and an icon and is announced through an aria-live region when it changes.
  • Read-aloud of lesson sections with the browser's speechSynthesis (English or Hindi voice where available), with a clear message when the API is missing.
  • Dyslexia-friendly mode (wider letter and word spacing, taller lines, shorter measure, optional switch to a sans-serif system font), high-contrast palette, reduced motion (also honours the OS setting), text scale from 90 % to 140 %, and two reading levels for every explanation.
  • Colour never carries meaning alone: badges and status pills carry words; quiz feedback has text.
  • Layout tested at 375 px, 768 px and desktop widths and at 200 % zoom.

Low-bandwidth mode

Switching it on: skips the web-font stylesheet on the next load, replaces illustrations with their text descriptions, disables animations and shadows, halves canvas resolution and caps simulations at 12 fps. There are no raster images in JANUS at all. The Settings page shows an estimate of data saved: the font size is measured from resource timing the first time fonts actually load on a device (otherwise a stated estimate is used), and illustration bytes are summed from the SVG markup that was not rendered.

Limitations

  • No server: sync is exercised end to end but ends in a local mirror. Multi-device sync would need an HTTP target and account model.
  • The tutor cannot reason, generalise or answer beyond the installed text. Questions phrased very differently from the source may match weakly; it will say so.
  • Hindi: 4 of 16 lessons have full text; the rest have Hindi titles and summaries only. Quiz and problem text is English-only. The Hindi voice for read-aloud depends on the device.
  • The check question after "I don't understand step N" only marks numeric answers automatically.
  • Storage is per browser profile. Clearing site data removes packs and progress; export them first.
  • Safari limits service-worker and IndexedDB lifetime for sites not added to the home screen.
  • Physics content assumes no air resistance and constant g; chemistry uses Ar values rounded to the integer commonly used in school work (Cl = 35.5).

Testing it

  1. Open the app while online. The status strip should move from installing to ready.
  2. Go to Packs, choose subjects and a limit, and Download to this device. Try a 64 KB limit to see lessons being cut.
  3. In Settings, switch on Simulate offline. The header pill changes to OFFLINE (simulated); or use your browser's offline mode / flight mode.
  4. Open a lesson, mark a section read, take the quiz. The sync queue count rises.
  5. Open the tutor and ask: why does a satellite stay in orbit, explain this in simpler language, give me a harder problem, quiz me.
  6. Switch Simulate offline off (or reconnect). Sync runs and the queue drains into the local mirror; the Notebook shows the time.