npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

playable-lessons

v0.6.4

Published

Turn educational source material into **playable interactive fiction**. Paste in a case study, lecture notes, a methodology, or just a topic, and Playable Lessons uses AI to weave it into a branching [Ink](https://www.inklestudios.com/ink/) story that lea

Readme

Playable Lessons

Turn educational source material into playable interactive fiction. Paste in a case study, lecture notes, a methodology, or just a topic, and Playable Lessons uses AI to weave it into a branching Ink story that learners can play — then export it as a self-contained HTML file, an Ink/Twee source file, or a printable walkthrough. It can also turn the same material into study flashcards, a multiple-choice quiz, a study summary, AI-collaboration tasks, or a teaching case study. A Plan mode recommends which outputs suit your material and can generate them all at once.

It ships in two forms:

  • Desktop app (Electron + React) — the full authoring experience: AI generation with an interactive clarification step, a node-graph editor, live player, a Projects dashboard (save / reopen / delete lessons as ownable folders on disk), inline editing with AI refinement (✨), and exports. Stories can be illustrated with an AI-generated image per scene (cartoon or photorealistic) using your own image provider — OpenAI, Google Imagen, or any OpenAI-compatible endpoint. Aimed at non-technical authors (e.g. educators).
  • CLI (playable-lessons, on npm) — the same engine, headless: generate an Ink story, flashcards, a quiz, a summary, AI-collaboration tasks, or a case study; plan a recommended set; validate, and export. Handy for scripts, CI, batch runs, and local/offline generation against Ollama.
  • Self-hosted web (Docker) — a browser-based version for institutions (e.g. university staff). The server holds the API key; users just enter an access code. No per-user keys, no accounts — work is stored per-browser.

The desktop app and the CLI share one generation pipeline; the app adds the interactive clarification step, visual editor, and inline editing on top.

A public landing page with a try-it demo (bring your own key) is at michael-borck.github.io/playable-lessons.


CLI

Install

npm install -g playable-lessons

Generate a story from source material

# Local + offline against Ollama (no API key needed):
playable-lessons generate --input notes.md --provider ollama --model llama3.1:8b --output lesson/

# Anthropic Claude (key from the environment):
ANTHROPIC_API_KEY=sk-ant-... playable-lessons generate -i case-study.md -p claude -o lesson/

# From a topic instead of a file, choosing formats:
playable-lessons generate --topic "Phishing awareness for staff" -p openai -o lesson/ -f ink,html,json

# Pipe source material via stdin:
cat lecture.txt | playable-lessons generate -o lesson/ -p ollama -m llama3.1:8b

# With a generated illustration per scene (cartoon or photorealistic):
OPENAI_API_KEY=sk-... playable-lessons generate -i notes.md -p ollama -o lesson/ --images --image-style cartoon

--images adds a # IMAGE_PROMPT: tag to every scene, then generates each image with an image provider (--image-provider openai|gemini|custom, inferred from OPENAI_API_KEY/GEMINI_API_KEY/--image-base-url; --image-model to override the default model). Images are embedded in the HTML export and written to an images/ folder.

Provider selection and API keys:

| Provider | --provider | Key (env var) | Notes | | --- | --- | --- | --- | | Claude | claude | ANTHROPIC_API_KEY | default model claude-opus-4-8 | | OpenAI | openai | OPENAI_API_KEY | default model gpt-4o | | Ollama | ollama | none (or OLLAMA_API_KEY) | local by default; --ollama-url for remote | | Custom | custom | PLAYABLE_LESSONS_API_KEY / OPENAI_API_KEY | OpenAI-compatible; requires --base-url + --model |

If --provider is omitted it's inferred from the environment (Claude → OpenAI → Ollama). Keys are read from env vars only — the CLI never touches the OS keychain (that's the desktop app). Other flags: --mode, --length (short/medium/long), --tone, --protagonist, --answers, --title, and --target (story | flashcards | quiz | summary | ai-task | case-study — see the per-target sections below). Run playable-lessons generate --help for all options.

Generate flashcards

Add --target flashcards to turn the same source material into a deck of study flashcards instead of an Ink story. Each card has a front, back, optional hint, and optional tag.

# Flashcards from a topic, offline via Ollama (no API key needed):
playable-lessons generate --topic "Supply chain resilience" --target flashcards -p ollama -m llama3.1:8b -o deck/

# From a file, exporting every format and setting the card count:
playable-lessons generate -i notes.md --target flashcards --cards 20 -o deck/ -f csv,html,anki,json

| Format | Output | | ------ | ------ | | csv | front,back,hint,tag — imports into Anki, Quizlet, Excel (.flashcards.csv) | | html | Self-contained flip-card deck (.flashcards.html) — click or Space to flip, arrow keys to navigate; works offline | | anki | Tab-separated import for Anki: front, back, tag (.flashcards.txt) | | json | The raw structured deck (.flashcards.json) |

The default format for flashcards is csv,html; use --cards N to set the count (default 12). The same source can yield both a story and a deck — run generate twice with different --target.

Generate a quiz

Add --target quiz to produce a self-marking multiple-choice quiz from the same source material. Each question has a stem, 2–6 options, the correct option's index, and an optional explanation.

playable-lessons generate --topic "Supply chain resilience" --target quiz -p ollama -m llama3.1:8b -o quiz/

playable-lessons generate -i notes.md --target quiz --cards 20 -o quiz/ -f html,json,txt

| Format | Output | | ------ | ------ | | html | Self-contained interactive quiz (.quiz.html) — pick answers, submit for an instant score, with correct/wrong highlighting and explanations; works offline | | json | The raw structured quiz (.quiz.json) | | txt | A printable quiz with an answer key (.quiz.txt) |

The default format for quiz is html,json; --cards N sets the number of questions.

Generate a summary

Add --target summary to distill the source material into a study summary: a short overview, the key points, and a glossary of essential terms.

playable-lessons generate --topic "Supply chain resilience" --target summary -p ollama -m llama3.1:8b -o summary/

playable-lessons generate -i notes.md --target summary --cards 10 -o summary/ -f html,txt,json

| Format | Output | | ------ | ------ | | html | Self-contained study sheet (.summary.html) — overview, numbered key points, glossary; works offline | | txt | A printable summary with key points and a glossary (.summary.txt) | | json | The raw structured summary (.summary.json) |

The default format for summary is html,txt; --cards N sets the target number of key points.

Generate AI-collaboration tasks

Add --target ai-task to re-engineer the material into AI-collaboration tasks — tasks a learner completes by iteratively interrogating an LLM chatbot. Each task is built so the chatbot's first answer is wrong or incomplete until the learner probes it: a concrete scenario with 2–4 load-bearing specifics, a student-facing brief, a deliverable, an engagement-anchored rubric (not a correctness rubric), and a note on why a delegating learner loses. (This output never produces quizzes/flashcards/summaries — pick those targets for those.)

playable-lessons generate --topic "clinic outreach on a budget cut" --target ai-task -p ollama -m llama3.1:8b -o tasks/ --cards 3

playable-lessons generate -i worksheet.md --target ai-task -o tasks/ -f html,txt,json

| Format | Output | | ------ | ------ | | html | Self-contained task sheet (.ai-task.html) — scenario, brief, deliverable, load-bearing specifics, rubric; works offline | | txt | A printable version of the tasks (.ai-task.txt) | | json | The raw structured task set (.ai-task.json) |

The default format for ai-task is html,txt; --cards N sets the number of tasks (try --cards 3 for richer tasks).

Generate a case study

Add --target case-study to turn the material into a teaching case study — a realistic situation (protagonist, situation, key facts, conflict, decision points, discussion questions) a learner analyzes. Use --depth to control how much is produced:

| depth | produces | | --- | --- | | idea | a brief premise (protagonist, situation, conflict) | | outline | the full structured skeleton, no prose | | complete (default) | the full skeleton plus a written narrative |

playable-lessons generate --topic "clinic scheduling under staff shortages" --target case-study --depth complete -p ollama -m llama3.1:8b -o case/

playable-lessons generate -i notes.md --target case-study --depth outline -o case/ -f html,txt,json

| Format | Output | | ------ | ------ | | html | Self-contained case-study sheet (.case-study.html) — narrative + structured sections; works offline | | txt | A printable version of the case study (.case-study.txt) | | json | The raw structured case study (.case-study.json) |

The default format for case-study is html,txt; --depth selects idea | outline | complete (default complete).

Plan a set of outputs (recommender)

Not sure which outputs to make? plan analyzes the material and recommends a complementary set (2–4 of the targets above), then optionally generates them all into the output folder.

# Just recommend a set:
playable-lessons plan --topic "supply chain resilience" -p ollama -m llama3.1:8b -o plan/

# Recommend AND generate every recommended output:
playable-lessons plan -i notes.md --apply -o plan/

plan writes plan.json + plan.txt + plan.html (the recommendation). With --apply it also generates each recommended target (.html + .json). In the desktop app, the Plan target shows the recommendation with a “Generate all → new project” action that writes the whole set to a project folder.

Validate an Ink file

playable-lessons validate --input story.ink

Export

# Standalone playable HTML
playable-lessons export --input story.ink --output dist/ --format html --title "My Lesson"

# Compiled Ink JSON
playable-lessons export --input story.ink --output dist/ --format json

# Multiple formats at once
playable-lessons export -i story.ink -o dist/ -f html,json,ink

| Format | Output | | ------ | ------ | | html | Self-contained player (.html) — works offline, share via LMS/email | | json | Compiled Ink story JSON | | ink | The raw Ink source (copied through) |

Run playable-lessons --help for all options.


Desktop app (development)

npm install --legacy-peer-deps
npm run dev          # launch the Electron app with hot reload
npm run build        # production build
npm run build:mac    # / build:win / build:linux — package installers
npm run typecheck
npm test

AI providers

The desktop app supports Claude, OpenAI, a local Ollama, and any OpenAI-compatible endpoint (remote Ollama, OpenRouter, LiteLLM, vLLM, …) via a base URL + bearer token. Model IDs are editable, with live model listing and a connection test. API keys are stored in your OS keychain (via Electron safeStorage), not in plain config.

Inline editing + AI refinement

After generating any output (flashcards, quiz, summary, AI-collaboration tasks, case study), switch to Study → Edit to tweak the content inline — edit text fields, add/delete items, and use the button to AI-refine individual fields. Click 💬 before ✨ to give a custom direction (e.g. "simplify for beginners"). Changes flow through to exports.


Self-hosted web (Docker)

For institutions or teams that want a browser-based version without per-user API keys. The server holds the key; users enter a shared access code.

Quick start

# 1. Grab the files
curl -sL https://raw.githubusercontent.com/michael-borck/playable-lessons/main/docker-compose.yml -o docker-compose.yml
mkdir -p server
curl -sL https://raw.githubusercontent.com/michael-borck/playable-lessons/main/server/.env.example -o server/.env

# 2. Edit server/.env — set your API key + optional access code
#    ANTHROPIC_API_KEY=sk-ant-...
#    ACCESS_CODE=staff-2024   (comma-separated for multiple groups; blank = no gate)

# 3. Swap build for the prebuilt image in docker-compose.yml
#    image: ghcr.io/michael-borck/playable-lessons:latest

# 4. Deploy
docker compose pull && docker compose up -d

Staff access the server at http://your-server:3000, enter the access code, and generate. Work is stored per-browser (localStorage); the AI key stays server-side. Rate limiting is configurable (RATE_LIMIT_PER_HOUR, default 30) and counts only generation submissions, not status polling.

Generations run as an in-process job queue (one at a time by default; GENERATE_CONCURRENCY to raise). Visitors see their queue position and live pipeline progress, can cancel while waiting (a job already running plays out — the pipeline has no abort switch), and can close the page and come back — results stay fetchable for an hour via a job id in localStorage. Jobs live in memory: a server restart clears the queue, and the UI says so honestly.

To revoke a group: remove their code from .envdocker compose restart. To update: change the image tag → docker compose pull && docker compose up -d.

Scene images (optional)

The hosted app can offer "illustrate every scene" (cartoon or photorealistic) on stories. It's off by default; enable it in .env with SCENE_IMAGES=true plus one image provider:

# Option A — Google Imagen (cheap, fast, ~$0.02/image; good default for a public server):
SCENE_IMAGES=true
GEMINI_API_KEY=AIza...

# Option B — OpenAI (gpt-image-1; reuses OPENAI_API_KEY if IMAGE_API_KEY is blank):
# SCENE_IMAGES=true
# IMAGE_PROVIDER=openai

# Option C — self-hosted generator on the same VPS (LocalAI, included in the compose file):
# docker compose --profile images up -d
# SCENE_IMAGES=true
# IMAGE_PROVIDER=custom
# IMAGE_BASE_URL=http://localai:8080/v1
# IMAGE_MODEL=stablediffusion

# Option D — your own SwarmUI server (GPU, free per image, native API):
# SCENE_IMAGES=true
# IMAGE_PROVIDER=swarmui
# IMAGE_BASE_URL=https://swarmui.example.org
# IMAGE_MODEL=juggernautXL_v9
# IMAGE_API_KEY=...        # only if your SwarmUI is behind auth

Notes for operators:

  • Each illustrated story triggers up to IMAGE_MAX_SCENES image generations (default 8) — combined with the per-IP RATE_LIMIT_PER_HOUR, that bounds your image spend.
  • The LocalAI service runs CPU-only by default and is slow (a minute or more per scene on a small VPS). It's internal-only (no published port — LocalAI has no auth). For usable self-hosting, use a GPU host and the localai/localai:*-gpu-* images; for a CPU-only VPS, prefer Gemini/OpenAI.
  • First LocalAI start downloads a multi-GB model into the localai-models volume.
  • If the app sits behind nginx/Caddy, raise proxy_read_timeout (e.g. 300s) — illustrated stories take minutes — and set TRUST_PROXY=1 so the per-IP rate limiter sees real visitor IPs instead of one shared proxy bucket.

Tech stack

Electron + electron-vite · React 19 + TypeScript · Zustand · inkjs (Ink compiler + runtime) · @xyflow/react (graph editor) · yargs (CLI) · Express (self-hosted server).

License

MIT © michael-borck