Small, dependency-free pieces for single-file browser games, taken out of Apple Press (play it) and Fancy, the Banana (play it) and cleaned up so anyone, or any AI helper working with a human, can drop them into another project.
MIT License. Copyright (c) 2026 LFC Foundry. Use, change and sell anything built with it. Keeping the license line is the only ask.
Live demos: https://lfcfoundry.github.io/JS-modules/
Every piece in one place, by name. The tables below give the version, the sizes (readable and gzipped) and what each piece does.
Modules & Tools (39)
- Device and browser adaptation: env-probes, frame-kit, fx-governor, slow-note
- Saving and data safety: save-kit, tab-guard
- Games - Looks and effects: cartoon-3d-kit, fly-to-mini, sprite-baker, stage-curtains, sticker-text
- Games - Controls: back-close, hold-press, pad-nav, wake-keeper
- Games - Assist: grown-ups-door, spotlight-tour
- Games - Math: idle-core
- Games - Testing: canvas-probe, firefox-stand-in, fuzz-kit, speed-lab, storage-probe, test-isolation, test-runner, webkit-driver
- App - Efficiency: memory-compression, single-file-tools
- Model simulation: walker-sim
- Visualization: gif-writer, netflow-engine, netflow-layers, netflow-sankey
- Size and packaging: lzma-decode, lzma-pack, pcodec-js, ppmd-decode, ppmd-pack, zip-open
Story editor packages (10)
- Story data: story-diff, story-format, story-outline, twine-import
- Story validation: story-check
- Story flow: story-map
- Story build and distribute: story-player
- Story packaging: zip-page
- Story testing: contract-kit, qa-kit
AI knowledge base (1)
- Working with an AI helper: ai-collab-kit
Grouped by what a piece does. The live page, with demos and a quick finder, is https://lfcfoundry.github.io/JS-modules/.
How to read the tables. Version is the label of the newest release; its first number is the API number, so the same first number means a drop-in copy. Size is the file as it sits in the repository (readable, with comments); Gzipped is that file compressed. A piece marked (tool) is for building and testing: it is not shipped inside a game or story. Story Editor packages are measured on their built global file. Every table has the same columns. Minified sizes and lean builds are in each piece's own README. Written by python3 tools/versions/versions.py sizes.
Every piece has a size.
Make a game behave on slow phones, privacy-focused browsers and power-saving screens.
| Name | Version | Size (KB) | Gzipped (KB) | What it does | Demo |
|---|---|---|---|---|---|
| env-probes | 1.1.0 | 16.2 | 5.4 | Detects privacy modes that blur timers, scramble canvas reads or block storage. | Demo |
| frame-kit | 1.2.1 | 38.9 | 12.1 | Frame pacing for canvas games: smooth frame time, effect governor, job pump, frame meter. | Demo |
| fx-governor | 1.1.1 | 6.2 | 2.5 | Steps visual effects down when the device cannot keep up; never back up mid-play. | Demo |
| slow-note | 1.0.0 | 9.7 | 3.7 | A short private note of errors, slow taps and long frames, copyable as text. | Demo |
Keep a player's progress safe and let them move it between devices.
| Name | Version | Size (KB) | Gzipped (KB) | What it does | Demo |
|---|---|---|---|---|---|
| save-kit | 1.2.1 | 26.5 | 9.1 | Safe saves in localStorage: backup slot, checksums, copy-paste save codes. | Demo |
| tab-guard | 1.1.0 | 9.6 | 3.4 | Keeps one tab in charge so two tabs never overwrite each other's saves. | Demo |
Curtains, flying elements, lettering and 3D-looking sprites.
| Name | Version | Size (KB) | Gzipped (KB) | What it does | Demo |
|---|---|---|---|---|---|
| cartoon-3d-kit | 1.2.0 | 49.3 | 20.2 | A written style guide and demo for the soft, glossy cartoon 3D look in CSS and SVG. | Demo |
| fly-to-mini | 1.2.0 | 18.1 | 6.3 | A big element scrolls away and a copy flies to its sticky mini version. | Demo |
| sprite-baker | 1.7.0 | 54.5 | 17.9 | Tiny software 3D rasterizer that bakes shaded sprites to canvases. | Demo |
| stage-curtains | 1.2.0 | 36.1 | 13.1 | Velvet stage curtains that slide shut over a page and fly open. | Demo |
| sticker-text | 1.1.0 | 5.0 | 2.1 | Die-cut sticker lettering in plain CSS, no images. | Demo |
Let players hold a button, or play with a gamepad.
| Name | Version | Size (KB) | Gzipped (KB) | What it does | Demo |
|---|---|---|---|---|---|
| back-close | 1.2.0 | 9.1 | 3.1 | Makes the phone's back swipe close the top panel instead of leaving the page. | Demo |
| hold-press | 1.1.0 | 13.0 | 4.1 | Hold-to-repeat buttons that survive Android Chrome's pointercancel. | Demo |
| pad-nav | 1.1.0 | 29.2 | 9.6 | Gamepad to focus: move through buttons, menus and sliders with a controller. | Demo |
| wake-keeper | 1.1.0 | 8.2 | 2.7 | Keeps the screen awake only while a game wants it. | Demo |
Show a new player around, and keep small children away from the buttons that matter.
| Name | Version | Size (KB) | Gzipped (KB) | What it does | Demo |
|---|---|---|---|---|---|
| grown-ups-door | 1.0.0 | 14.8 | 5.1 | A hold-and-drag task a young child will not finish, in front of grown-up buttons. | Demo |
| spotlight-tour | 1.1.0 | 12.3 | 4.7 | A first-visit tour: dimmed page, bright window, Skip and Back, remembers who saw it. | Demo |
The numbers behind idle and clicker games.
| Name | Version | Size (KB) | Gzipped (KB) | What it does | Demo |
|---|---|---|---|---|---|
| idle-core | 1.2.0 | 22.8 | 7.7 | Tested maths for idle and clicker games: costs, buy-max, away income, number format. | Demo |
Test a game by playing it at random, shrinking any failure to a short replay, counting what it draws and driving real browsers.
| Name | Version | Size (KB) | Gzipped (KB) | What it does | Demo |
|---|---|---|---|---|---|
| canvas-probe (tool) | 1.1.0 | 28.8 | 8.8 | Test-build counter for canvas 2D games: calls per frame, leaks, repeated style writes. | — |
| firefox-stand-in (tool) | 1.0.0 | 11.0 | 4.0 | Runs a Playwright-for-Python test in stock Firefox without rewriting it. | — |
| fuzz-kit (tool) | 1.1.0 | 78.3 | 25.4 | Seeded random-input fuzzer for games, with failure shrinking and browser runners. | — |
| speed-lab (tool) | 1.1.1 | 20.5 | 7.4 | A one-button speed test on the device: switches parts off and says what costs time. | Demo |
| storage-probe (tool) | 1.1.0 | 19.4 | 7.2 | A one-button page that says whether copies of a page share their saved data. | Demo |
| test-isolation (tool) | 1.1.0 | 6.5 | 2.4 | Prefixes every localStorage key on a test page so tests never touch the real save. | — |
| test-runner (tool) | 1.0.0 | 34.0 | 11.8 | Warm browsers, fair timing, skipped rebuilds, wasm-vs-JS twin test, picture gate and bisect. | — |
| webkit-driver (tool) | 1.3.0 | 22.2 | 8.1 | Drives WebKitGTK or GNOME Web from Node with Playwright-style calls, no dependencies. | — |
Keep a one-file app small and light: smaller data in memory, smaller pages on the wire.
| Name | Version | Size (KB) | Gzipped (KB) | What it does | Demo |
|---|---|---|---|---|---|
| memory-compression | 2.0.3 | 274.3 | 84.6 | Keeps big arrays, bytes and lists of objects packed in memory and brings them back exactly. | Demo |
| single-file-tools (tool) | 1.1.0 | 27.5 | 10.4 | Font subsetting, safe inline-SVG tightening and a Zopfli self-extracting packer. | — |
Simulate random walkers over a graph.
| Name | Version | Size (KB) | Gzipped (KB) | What it does | Demo |
|---|---|---|---|---|---|
| walker-sim | 2.2.0 | 64.9 | 21.0 | Simulates random walkers on a network: seeded runs, 95% ranges, exact odds. | Demo |
Draw maps, flow charts and animated pictures from data.
| Name | Version | Size (KB) | Gzipped (KB) | What it does | Demo |
|---|---|---|---|---|---|
| gif-writer | 1.2.0 | 40.0 | 15.9 | Writes animated GIFs from canvas frames with one shared palette. | Demo |
| netflow-engine | 2.1.0 | 200.3 | 62.3 | Netflow Map's engine as an adaptable module: nine views, network layouts, add-on hand-over. | Demo |
| netflow-layers | 1.3.0 | 29.1 | 9.4 | Lays out a graph in rows, with tags for long links and tall maps cut into columns. | Demo |
| netflow-sankey | 2.3.0 | 48.2 | 17.3 | Lays out a Sankey flow chart from a list of links, with an SVG drawer. | Demo |
Open zip and Excel files safely, and pack a finished page into a smaller file that unpacks itself.
| Name | Version | Size (KB) | Gzipped (KB) | What it does | Demo |
|---|---|---|---|---|---|
| lzma-decode | 1.3.0 | 22.2 | 8.7 | LZMA decoder: WebAssembly first, small readable JavaScript fallback; tested against Python's lzma. | Demo |
| lzma-pack (tool) | 2.0.0 | 27.7 | 10.6 | Packs one HTML file into a smaller self-unpacking file with LZMA (Python 3). | — |
| pcodec-js | 1.1.0 | 171.9 | 69.0 | Pcodec number compression for web pages: unpack, and optionally pack, big lists of numbers. | Demo |
| ppmd-decode | 1.3.0 | 45.3 | 16.3 | A PPMd (7-Zip PPMd7) decoder: WebAssembly first, JavaScript fallback; smaller than LZMA. | Demo |
| ppmd-pack (tool) | 2.0.0 | 47.8 | 14.6 | Packs one HTML file into a self-unpacking file with PPMd (pure Python). | — |
| zip-open | 1.2.0 | 30.7 | 10.8 | Opens .zip and .xlsx files safely, with limits counted on what really unpacks. | Demo |
Packages for interactive stories. They live with the Story Editor they are generated from, under packages/.
Create, clean, import and save stories in one shared file format.
| Name | Version | Size (KB) | Gzipped (KB) | What it does | Demo |
|---|---|---|---|---|---|
| story-diff | 1.0.0 | 6.1 | 2.0 | Says in plain words what one edit did to a story. | — |
| story-format | 2.0.0 | 49.4 | 14.0 | Create, clean, search, import and export interactive stories as JSON, with a schema. | — |
| story-outline | 1.0.0 | 25.7 | 7.9 | Turns an indented outline into pages and choices, or a whole branching story. | — |
| twine-import | 1.0.0 | 93.4 | 25.0 | Reads Twine stories (Twine 2 HTML, Twee 3, Twine 1) and turns their code into rules. | — |
Find dead ends and endings nobody can reach before a reader does.
| Name | Version | Size (KB) | Gzipped (KB) | What it does | Demo |
|---|---|---|---|---|---|
| story-check | 1.0.0 | 72.0 | 20.0 | Exact checker for branching stories: dead ends and endings no player can reach. | — |
See a story's shape: maps of its pages and simulated readers.
| Name | Version | Size (KB) | Gzipped (KB) | What it does | Demo |
|---|---|---|---|---|---|
| story-map | 1.0.0 | 60.0 | 17.8 | Layered, arc and matrix drawings of a story graph, plus simulated players. | — |
Turn a story into a game, or play it from code or a terminal.
| Name | Version | Size (KB) | Gzipped (KB) | What it does | Demo |
|---|---|---|---|---|---|
| story-player | 1.0.1 | 73.3 | 24.2 | Builds one offline HTML game from a story, or plays a story from code or a terminal. | — |
Pack a finished story page into a smaller page.
| Name | Version | Size (KB) | Gzipped (KB) | What it does | Demo |
|---|---|---|---|---|---|
| zip-page | 1.0.0 | 6.5 | 2.7 | Shrinks a finished HTML page into a self-unzipping page. | — |
Run tests on stock Firefox, generate random stories and keep two implementations in step.
| Name | Version | Size (KB) | Gzipped (KB) | What it does | Demo |
|---|---|---|---|---|---|
| contract-kit | 1.1.0 | 16.3 | 5.7 | Keeps two implementations of one rule set in step: random inputs, side-by-side comparison. | — |
| qa-kit | 0.2.1 | 93.1 | 30.3 | Tiered test runner and Playwright-style API for stock Firefox, plus a random-story generator. | — |
A written process for a person and an AI assistant building software together.
| Name | Version | Size (KB) | Gzipped (KB) | What it does | Demo |
|---|---|---|---|---|---|
| ai-collab-kit | — | 13.8 | 6.3 | A written process and templates for a person and an AI assistant building software together. | — |
Every module is no dependencies and, apart from frame-kit (four small files, each usable alone), one file, and works two ways:
<script src="modules/save-kit/save-kit.js"></script>
<script>var kit = SaveKit.create({ key: 'mygame', schema: { score: 'num' } });</script>const SaveKit = require('./modules/save-kit/save-kit.js'); // Node / bundlersCopy just the file you need. Each folder has a README (what it does, how it differs from what already exists, limits) and a demo.html you can open straight from disk.
Only parts that are not already well covered by open source were kept. Large general libraries (animation, audio, physics, fuzzers, test runners) are not repeated here. What is here are the small, specific fixes learned by shipping one real game to phones and desktops: a curtain half stuck out of place on Android, a hold that dies on pointercancel, a save that was overwritten by a NaN, Firefox privacy mode rounding the clock to 100 ms. Each module README names the closest existing project and says how this one differs.
The games' own art, characters, text, sounds and secret content stay in Apple Press and Fancy, the Banana and are not licensed here. cartoon-3d-kit teaches the technique with a generic blob character instead.
npm i playwright-core
CHROMIUM=/path/to/chrome node tests/run.js # CHROMIUM is required: the browser tests refuse to run without it and print the real Chromium version (see Test browsers below)
npm run test:kits # slow-note, spotlight-tour, speed-lab, test-isolation, gif-writer, zip-open, memory-compression, netflow-sankey, walker-sim, frame-kit, sprite-baker, pad-nav, tab-guard, back-close, lzma, fuzz-kit, canvas-probe, storage-probe, firefox-stand-in, webkit-driver (needs python3 and Playwright for Python for the browser parts)
29 checks: maths and save logic in Node, the browser pieces (curtains, hold-press, fly-to-mini, probes, governor, sticker text) in Chromium. For humans and AI helpers: read AGENTS.md, llms.txt and modules.json. What changed in each version: CHANGELOG.md (and a CHANGELOG.md in every module and tool folder).
Browser checks run only in the browsers kept on the browsers branch of the private knowledge-base repository: Firefox 158 (the stress test), ungoogled Chromium 155 and WebKitGTK 2.52.6 (GNOME Web 46.5), on Linux. The branch can change (a version updated, a browser added or removed), so read its README before each run and use what it says then. Every result in this repository's docs names the exact browser and version, for example "Firefox 158" or "Chromium 155 (ungoogled)".
Every module and tool says which browsers its release was tested in. The newest release's list is the Tested in line near the top of its README; each release's list is the Tested in line in its full changelog entry, which the maintainers keep privately (the repo's CHANGELOG.md files are the short end-user versions). A line names each browser with its version, says "not recorded" where the old records do not show it, and says what was not run. Older builds (Chromium 141 and 153, Firefox 155 and 157) were stand-ins before this rule; entries that were tested on them still say so, and are not re-measured unless a change needs it. versions.py set ... --tested "..." writes the line; see tools/versions/README.md.
Rules for anyone running the tests:
- Run one browser at a time. Two at once make timing checks flaky.
- Point the test at the browser you installed:
CHROMIUM=/path/to/chromefor the Chromium checks,FIREFOX_PATH(andGECKODRIVER_PATHfor firefox-stand-in) for Firefox. A bare run that falls back to some other build gives unfair timing numbers, so say which one ran. - WebKitGTK and GNOME Web can only be driven through WebDriver (Playwright and Puppeteer cannot); the recipe is in the branch README.
- Not covered yet: Safari itself, and real phones (the Pixel and an iPhone). A passing desktop run is not a phone result.
- Apple Press: a clicker game for kids (play)
- Story Editor: an offline editor for branching stories, plus MIT packages cut from it: story format and checker, game builder and player, story map layout, a self-unzipping page packer, and a Firefox test kit (try it)
- Fancy Banana: a one-tap flying-banana arcade game (play)
- Pineapple Defense: a flick-to-throw pineapples-at-penguins game that uses a few of these pieces (play)
- JS-modules: this repository (demos)