🕉️ Devlog — गुरु-दीक्षा: Teaching Karma Without Breaking Immersion

"गुरु बिना ज्ञान नहीं।"
Without a Guru, there is no knowledge.


The Problem

Moksha is a game rooted in Sanatan Shastra — Vedic Karma mechanics, Sanskrit concepts, rebirth cycles. It's intentionally deep. And that depth was quietly becoming its biggest barrier.

New players would start the game and immediately face naama-jaap, vairaagya, prarabdha, chetana-jagriti — all at once, with no guidance. Within the first 30 seconds, most had no idea what they were doing or why.

The game needed a tutorial. But it needed one that didn't betray what Moksha is.


Why a Normal Tutorial Wouldn't Work

The obvious solution — pause the game, show a tooltip, unpause — felt completely wrong for Moksha. Spiritually, a hard pause breaks the flow of consciousness. Mechanically, isPaused = true is deeply wired into audio ducking, gamepad state, and ambient layers. Hijacking it for tutorial logic would have introduced subtle bugs across every system.

An earlier attempt at a tutorial (Issue #30) tried to live inside engine.js itself. That was worse — the engine is already the heaviest file in the codebase, and embedding tutorial step state there violated the entire modular architecture we'd been building toward.

So I scrapped both approaches and started over.


The Solution: गुरु-दीक्षा (Guru's Initiation)

The new system is built around one philosophical reframe: a Guru doesn't stop the world to teach. They walk alongside you.

This became the technical foundation too.

A New Module — src/tutorial.js

TutorialManager is a self-contained ES6 class. It doesn't import from engine.js or touch any game state directly. Instead, main.js passes it an engine state snapshot every frame via checkCompletion(state). The tutorial reads — never writes.

engine.js ──(no connection)──> tutorial.js
main.js   ──(snapshot feed)──> tutorial.js

Enter fullscreen mode Exit fullscreen mode

Zero coupling. Zero risk to existing systems.

Slow Motion, Not Hard Pause

When a tutorial card is visible, the game doesn't stop. Instead, dt is multiplied by 0.3 in the game loop:

const dt = tutorial.isSlowMode() ? rawDt * 0.3 : rawDt;

Enter fullscreen mode Exit fullscreen mode

The world keeps moving — entities drift, the cosmos breathes — but at a contemplative pace. It feels like the Guru has slowed time itself to let you absorb the teaching. As soon as you dismiss the card, full speed resumes instantly.

Forced Spawns — _forceSpawnMaya(type, x, y)

Some tutorial steps require specific entities to appear — a naama (ॐ) to collect, a specific Maya type to encounter. Random spawning can't be trusted for this.

A new method was added to KarmaMixin in karma.js:

_forceSpawnMaya(type, x, y) {
    // Deterministic pool slot allocation — no random, no push/splice
    for (let i = 0; i < this.mayaPool.length; i++) {
        if (!this.mayaPool[i].active) {
            // assign type + position directly
            break;
        }
    }
}

Enter fullscreen mode Exit fullscreen mode

It follows the same fixed-size object pool pattern used everywhere in the engine — no heap allocation, no GC pressure.

The 5 Steps

Step What You Do Shaastra
1 Move your Pankhudi (सारथी) "उद्धरेदात्मनात्मानं" — Gita 6.5
2 Collect a Naama (ॐ) "मायाजालमिदं विश्वं" — Yogavasishtha
3 Perform Naama-Jaap "नाम जपत मंगल" — Ramcharitmanas
4 Enter the Bhakti-Maarg "भक्त्या मामभिजानाति" — Gita 18.55
5 Learn about Prarabdha "भोगेन क्षीयते पापं" — Skanda Purana

Each card shows the relevant Sanskrit shloka with attribution — scripture style, not tooltip style. The last step is information-only (Prarabdha cannot be "completed," only endured — which is the point).

Scripture-Style Card Rendering

drawTutorialCard() in render.js renders a full overlay card:

  • Dark semi-transparent backdrop (game remains visible beneath)
  • Saffron accent line at the top
  • Gold 🕉️ header
  • Sanskrit shloka in italic serif
  • Source attribution (Gita, Upanishad, Purana)
  • Task text with multi-line support
  • Dismiss button: ENTER / TAP / Gamepad START
  • ESC skips the entire initiation

The card is visually distinct from the existing alert system — bigger, centered, designed to be read slowly.

Persistence

Once completed or skipped, localStorage records it:

localStorage.setItem('moksha_tutorial_seen', '1');

Enter fullscreen mode Exit fullscreen mode

The tutorial never shows again. Players who know what they're doing aren't forced to sit through it.


What This Closes

This implementation resolves Issue #37 and supersedes the earlier attempt in Issue #30. The checklist from the issue spec:

  • src/tutorial.js — TutorialManager
  • ✅ Slow-motion dt × 0.3 (not isPaused)
  • _forceSpawnMaya(type, x, y) in karma.js
  • ✅ Scripture-style dismissible card in render.js
  • localStorage persistence
  • ✅ ESC to skip
  • ✅ ENTER / click / Gamepad START to dismiss
  • ✅ Engine fully decoupled — engine.js untouched

Commit:

https://github.com/weirdcodesofficial/MOKSHA/commit/dca7f3e13d654644d61231233002b4e851bd42ac


What's Next

The tutorial lays the foundation for v0.1.0 — Pankhudi Directional Mechanics, where active pankhudi direction will drive power shoot, Maya pull, and Indriya pull. That's a significant gameplay overhaul and deserves its own devlog when the time comes.

For now — if you've played Moksha before and found it confusing, try it again. The Guru is waiting.


Moksha is a solo-developed browser game rooted in Sanatan Shastra.
Play it at weirdcodes.itch.io/moksha
Support the project at ko-fi.com/weirdcodes